home *** CD-ROM | disk | FTP | other *** search
/ Aminet 21 / Aminet 21 (1997)(GTI - Schatztruhe)[!][Oct 1997].iso / Aminet / dev / c / FW_ACMdev.lha / ACE-Devices (.txt) < prev   
Encoding:
Final Write Document  |  1997-07-18  |  928.2 KB  |  11,031 lines

  1. Chapter 1 "DEVICES"
  2.  
  3. 1.1  Introduction
  4. 1.2  Requests
  5. 1.2.1  IORequest
  6. 1.2.2  IOStdReq
  7. 1.2.3  IOExtReq
  8. 1.3  Prepare the Device
  9. 1.4  Standard Exec Commands
  10. 1.5  Send Requests
  11. 1.5.1  Synchronous Commands
  12. 1.5.2  Asynchronous Commands
  13. 1.5.2.1  Keep on Checking the Request Until It is Completed
  14. 1.5.2.2  Wait for the Request to be Completed
  15. 1.5.2.4  Wait for a Reply Message
  16. 1.5.3  Abort Requests
  17. 1.5.4  Quick I/O
  18. 1.6  Errors
  19. 1.7  Devices
  20. 1.7.1  Timer Device
  21. 1.7.2  Gameport Device
  22. 1.7.3  Audio Device
  23. 1.7.4  Narrator Device
  24. 1.7.5  Trackdisk Device
  25. 1.7.6  Serial Device   
  26. 1.7.7  Parallel Device
  27. 1.7.8  Printer Device
  28. 1.7.9  Keyboard Device
  29. 1.7.10  Input Device
  30. 1.7.11  Console Device
  31. 1.7.12  Clipboard Device
  32. 1.7.13  SCSI and PCMCIA Device
  33.  
  34. Chapter 2 "TIMER DEVICE"
  35.  
  36. 2.1  Introduction
  37. 2.2  Timer
  38. 2.2.1  Time Request
  39. 2.2.2  Open the Timer Device
  40. 2.2.3  Set Time Request
  41. 2.2.4  Clean Up
  42. 2.2.5  Example
  43. 2.3  System Time
  44. 2.3.1  Get System Time
  45. 2.3.2  Set System Time
  46. 2.4  Special Time Functions
  47. 2.4.1  Compare Times
  48. 2.4.2  Add Time
  49. 2.4.3  Subtract Time
  50. 2.4.5  Example
  51. 2.5  Functions
  52. 2.6  Examples
  53.  
  54. Chapter 3 "GAMEPORT DEVICE"
  55.  
  56. 3.1  Introduction
  57. 3.2  Common Input Devices for the Gameport
  58. 3.2.1  Mouse
  59. 3.2.2  Joystick
  60. 3.2.3  Proportional Joystick
  61. 3.2.4  Light Pen
  62. 3.2.5  Drawing (Digitizing) Tablet
  63. 3.2.5  Trackball
  64. 3.3  The Gameport Device
  65. 3.3.1  Create a Message Port
  66. 3.3.2  Allocate an I/O Request Block (Structure)
  67. 3.3.3  Open the Gameport Device
  68. 3.3.4  Check if Some Other Task is Already Using The Port
  69. 3.3.5  Set Type of Controller
  70. 3.3.6  Set Trigger
  71. 3.3.7  Prepare to Read
  72. 3.4  How to Monitor the Gameport
  73. 3.4.1  The InputEvent Structure
  74. 3.4.1  Collect Joystick Events
  75. 3.4.2  Collect Mouse Events
  76. 3.5  Functions
  77. 3.6  Examples
  78.  
  79. Chapter 4 "AUDIO DEVICE"
  80.  
  81. 4.1  Introduction
  82. 4.1.1  Sound
  83. 4.1.2  Different Waveforms
  84. 4.1.3  Digital and Analog Waveforms
  85. 4.1.4  Play Sampled Sounds or Create Your Own Tunes
  86. 4.2  Prepare the Audio Device
  87. 4.2.1  Priority
  88. 4.2.2  Allocating Channels
  89. 4.2.3  Create Waveforms
  90. 4.2.4  Notes and Frequencies
  91. 4.2.5  The Audio Request Block
  92. 4.2.6  Open the Audio Device  
  93. 4.2.7  Reserve Channels
  94. 4.2.8  Lock Channels
  95. 4.3  Use the Audio Device
  96. 4.3.1  Play Sounds
  97. 4.3.2  Use Several Request Blocks
  98. 4.3.2  Play Double Buffered Sounds
  99. 4.3.3  Modify the Hardware Registers
  100. 4.4  Clean Up Afterwards
  101. 4.4.1  Unlock Channels
  102. 4.4.2  Remove All Messages and Close the Reply Port
  103. 4.4.3  Close the Device
  104. 4.4.4  Deallocate the Request Blocks
  105. 4.4.5  Deallocate Sound Buffers
  106. 4.5  Audio Device Commands
  107. 4.5.1  General Device Commands
  108. 4.5.1.1  CMD_Write
  109. 4.5.1.2  CMD_Read
  110. 4.5.1.3  CMD_Stop
  111. 4.5.1.4  CMD_Start
  112. 4.5.1.5  CMD_Flush
  113. 4.5.1.6  CMD_Reset
  114. 4.5.2  Special Audio Device Commands
  115. 4.5.1.1  ADCMD_
  116. Allocate
  117. 4.5.1.2  ADCMD_F
  118. ree
  119. 4.5.1.3  ADCMD_SETPREC
  120. 4.5.1.4  ADCMD_FINISH
  121. 4.5.1.5  ADCMD_PERVOL
  122. 4.5.1.6  ADCMD_LOCK
  123. 4.5.1.7  ADCMD_WAITCYCLE
  124. 4.6  Functions
  125. 4.7  Examples
  126.  
  127. Chapter 5 "NARRATOR DEVICE"
  128.  
  129. 5.1  Introduction
  130. 5.2  Artificial Speech
  131. 5.2.1  Phonetic Symbols
  132. 5.2.2  Intonation
  133. 5.2.3  Punctuation and Special Symbols
  134. 5.2.4  Volume
  135. 5.3  Convert Text into Phonetic Symbols
  136. 5.3.1  Open the Translator Library
  137. 5.3.2  Translate Text
  138. 5.3.3  Close the Translator Library
  139. 5.4  Read Phonetic Symbols
  140. 5.4.1  Narrator Request Block
  141. 5.4.2  Open the Narrator Device
  142. 5.4.3  Read Phonetic Text
  143. 5.4.4  Using Several Request Blocks
  144. 5.4.5  Clean Up
  145. 5.4.5.1  Remove All Messages
  146. 5.4.5.2  Close Message Port
  147. 5.4.5.3  Close the Narrator Device
  148. 5.4.4  Deallocate the Request Blocks
  149. 5.5  The Narrator's Mouth
  150. 5.5.1  Mouth Request Block
  151. 5.5.2  Create a Mouth Request Block
  152. 5.5.3  Prepare the Mouth Request Block
  153. 5.5.4  Get the Size of the Mouth
  154. 5.6  Examples
  155.  
  156. Chapter 6 "TRACKDISK DEVICE"
  157.  
  158. 6.1  Introduction
  159. 6.2  Amiga Disk Drivers
  160. 6.3  Trackdisk Device
  161. 6.3.1  RequestBlock
  162. 6.3.2  Open the Trackdisk Device
  163. 6.3.3  Clean Up
  164. 6.4  Commands
  165. 6.4.1  Read
  166. 6.4.2  Write
  167. 6.4.3  Motor On/Off
  168. 6.4.4  Update the Disk
  169. 6.4.5  Clear Buffer
  170. 6.4.6  Position the Head
  171. 6.4.7  Format
  172. 6.4.8  Remove
  173. 6.4.9  Get the Disk's Current Number
  174. 6.4.10  Check if There Is a Disk in the Drive or Not.
  175. 6.4.11  Check if the Disk Is Write-Protected or Not
  176. 6.4.12  Get Drive Type
  177. 6.4.13  Get the Number of Tracks 
  178. 6.5  Errors
  179. 6.6  Examples
  180.  
  181. Chapter 7 "SERIAL DEVICE"
  182.  
  183. 7.1  Introduction
  184. 7.2  The Serial Port
  185. 7.2.1  Byte to Bits and Vice-Versa
  186. 7.2.2  Pin Assignment
  187. 7.2.3  The Data Stream
  188. 7.3  The Serial Device
  189. 7.3.1  Prepare the Serial Device
  190. 7.3.2  Open the Serial Device
  191. 7.3.3  Set Serial Parameters
  192. 7.3.4  Read Data
  193. 7.3.5  Write Data
  194. 7.3.6  Errors
  195. 7.3.7  Clean Up
  196. 7.4  A Complete Example
  197. 7.5  Other Useful Commands
  198. 7.5.1  Break
  199. 7.5.2  Clear
  200. 7.5.3  Flush
  201. 7.5.4  Query
  202. 7.5.5  Reset
  203. 7.5.6  Start
  204. 7.5.7  Stop
  205. 7.6  Functions
  206. 7.7  Commands
  207. 7.8  Examples
  208.  
  209. Chapter 8 "PARALLEL DEVICE"
  210.  
  211. 8.1  Introduction
  212. 8.2  Parallel Port
  213. 8.3  Parallel Device
  214. 8.3.1  The Parallel RequestBlock
  215. 8.3.2  Open the Parallel Device
  216. 8.3.3  Set Parallel Parameters
  217. 8.3.4  Write Data
  218. 8.3.5  Read Data
  219. 8.3.6  How to Handle Several Requests Simultaneously
  220. 8.3.7  Errors
  221. 8.3.8  Clean Up
  222. 8.4  A Complete Example
  223. 8.5  Other Useful Commands
  224. 8.5.1  Flush
  225. 8.5.2  Query
  226. 8.5.3  Reset
  227. 8.5.4  Start
  228. 8.5.5  Stop
  229. 8.6  Functions
  230. 8.7  Commands
  231. 8.8  Examples
  232.  
  233. Chapter 9 "PRINTER DEVICE"
  234.  
  235. 9.1  Introduction
  236. 9.2  Printer Device
  237. 9.2.1  The Printer Device's RequestBlocks
  238. 9.2.3  Print Text
  239. 9.2.4  Send Special Commands to the Printer
  240. 9.2.5  Print Graphics
  241. 9.2.6  Errors
  242. 9.2.7  Clean Up
  243. 9.3  A Complete Example
  244. 9.4  Other Useful Commands
  245. 9.4.1  Flush
  246. 9.4.2  Reset
  247. 9.4.3  Start
  248. 9.4.4  Stop
  249. 9.5  Functions
  250. 9.6  Commands
  251. 9.7  Examples
  252. TABLE OF CONTENTS
  253.  
  254. DEVICES
  255.  
  256. 1.1  INTRODUCTION
  257.  
  258. The Amiga's operating system must be able to handle input events from the 
  259. keyboard or gameports, monitoring the disk drivers, sending data to the 
  260. parallel device and communicating with the serial port, playing stereo sound 
  261. with its audio chips, etc... If this was not enough, all these things have to 
  262. be monitored in a multitasking environment where several tasks (programs) may 
  263. run simultaneously.
  264.  
  265. As you understand this is a very complicated system that controls all these 
  266. things. Despite the complexity it must also be very fast so as little 
  267. processing time as possible is used.
  268.  
  269. The part of the Amiga which takes care of all these routines is usually 
  270. referred as "Exec". Exec handles tasks and task-switching, interrupts, messages 
  271. and controls all Amiga's "devices".
  272.  
  273. In this chapter we will look at how you can, with help of Exec, control the 
  274. devices. A device is a collection of special routines which handles things like 
  275. disk drivers, serial and parallel ports, input events, sound generating etc... 
  276. Although all these routines are very different they are all handled in almost 
  277. the same manner. This means that once you know how to control one of the 
  278. devices, you do not need to read much more before you can control all of them.
  279.  
  280. 1.2  REQUESTS
  281.  
  282. When you want to do something with a device you fill a "request block" with 
  283. your requirements and send it to Exec. Exec will now take care of everything. 
  284. Once it has, successfully or not, completed your request it sends a message to 
  285. you which tells you that your request has been completed.
  286.  
  287. 1.2.1  IOREQUEST
  288.  
  289. The "request block" is actually an IORequest structure defined in header file 
  290. "exec/io.h". It looks like this:
  291.  
  292. struct IORequest
  293. {
  294.   struct Message io_Message;
  295.   struct Device  *io_Device;
  296.   struct Unit    *io_Unit;
  297.   UWORD  io_Command;
  298.   UBYTE  io_Flags;
  299.   BYTE   io_Error;
  300. };
  301.  
  302. io_Message:
  303.     
  304. The top part of the request block consists of a Message structure. It is used 
  305. by Exec when the request is sent around inside the Amiga. It is also used to 
  306. queue requests at a device's port.  You do not need to bother very much about 
  307. this part since Exec takes care of most things. You must however initialize it 
  308. correctly before you may start to use the request.
  309.  
  310. io_Device:
  311.     
  312. Pointer to the device which this request has been prepared for. Se below for 
  313. more information about how to initialize a request block.
  314.  
  315. io_Unit:
  316.     
  317. Some devices, the trackdisk device for example, consists of several units 
  318. (df0:, df1, and so on), and this part is used to identify which unit should be 
  319. affected.
  320. io_Command:
  321.     
  322. It is here you set your command. There exist both standard commands which are 
  323. handle by most devices, but there exist also a lot of special unique commands 
  324. for some devices. See below for more information about available commands.
  325.  
  326. io_Flags:
  327.     
  328. This field is used to set special flags.
  329.  
  330. io_Error:
  331.     
  332. When the request has been completed you can look at this field to see if your 
  333. request was successfully or not completed.
  334.  
  335. 1.2.2  IOSTDREQ
  336.  
  337. The IORequest structure is the shortest request block you may use, and consists 
  338. of only the most important fields. Normally you need to use a request block 
  339. which is a bit larger. Since it is the most commonly used request block, it is 
  340. called the "standard request block" (IOStdReq) and is defined like this: (Also 
  341. declared in the header file "exec/io.h")
  342.  
  343. struct IOStdReq
  344. {
  345.   struct Message io_Message;
  346.   struct Device  *io_Device;
  347.   struct Unit    *io_Unit;
  348.   UWORD  io_Command;
  349.   UBYTE  io_Flags;
  350.   BYTE   io_Error;
  351.  
  352.   ULONG  io_Actual;
  353.   ULONG  io_Length;
  354.   APTR   io_Data;
  355.   ULONG  io_Offset;
  356. };
  357.  
  358. As you can see the top part is identical to the IORequest structure, but there 
  359. have been four extra fields added.
  360.  
  361. io_Actual:
  362.     
  363. When your request has been complete you can look here to find the actual number 
  364. of bytes transferred. If this value is not the same as the number of bytes you 
  365. wanted to transfer you know something went wrong.
  366.  
  367. io_Length:
  368.     
  369. The number of bytes you want to transfer. If you want to continue to 
  370. send/retrieve information until some special external event, set this field to 
  371. -1.
  372.  
  373. io_Data:
  374.     
  375. If you are going to send (write) data, this should be a pointer to the data 
  376. that should be sent. If you want to retrieve (read) data this should be a 
  377. pointer to a memory buffer were all data which is collected should be store.
  378.  
  379. io_Offset:
  380.     
  381. Special offset value used by some devices.
  382.  
  383. To make life simpler there exist a special function which will automatically 
  384. allocate and preinitialize the request block. The function is called 
  385. CreateStdIO(). However, before you may use this function you have to create a 
  386. message port to which the device can communicate with you. Simply use the 
  387. CreatePort() function which is described in the "System" manual, chapter 
  388. "Messages".
  389.  
  390. CreatePort() allocates and initializes a MsgPort structure. Normally the 
  391. message port should only be used by you and the device, so you do not need to 
  392. give it any name. The priority should usually be set to 0 (normal priority).
  393.  
  394. Synopsis
  395. :
  396.     
  397. msg_port = CreatePort( name, pri );
  398.   
  399. msg_port:
  400.     
  401. (struct MsgPort *) Pointer to the new MsgPort structure, or NULL if something 
  402. went wrong.
  403.  
  404. name:
  405.     
  406. (char *) Pointer to a string containing the name of the message port, or NULL. 
  407. When working with devices you normally do not need to use any name.
  408.   
  409. msgp:
  410.     
  411. (struct MsgPort *) Pointer to the MsgPort structure that should be allocated.
  412.  
  413. pri:
  414.     
  415. (BYTE) This message port's priority. Usually set to 0 (normal priority).
  416.  
  417. Once you have successfully created the reply port you can allocate and 
  418. initialize the standard request block with help of the CreateStdIO() function. 
  419. Give it a pointer to the reply port as the only parameter.
  420.  
  421. Synopsis
  422. :
  423.     
  424. std_req = CreateStdIO( msg_port );
  425.  
  426. std_req:
  427.     
  428. (struct IORequest *) Pointer to the new standard request block (struct IOStdReq 
  429. *), or NULL if the request block could not be created.
  430.  
  431. msg_port:
  432.     
  433. (struct MsgPort *) Pointer to the message port the device should use to 
  434. communicate with you.
  435.  
  436. When your program terminates you must delete all request blocks you have 
  437. created. Use the DeleteStdIO() function.
  438.  
  439. Synopsis
  440. :
  441.     
  442. DeleteStdIO( std_req );
  443.  
  444. std_req:
  445.     
  446. (struct IOStdReq *) Pointer to the standard request block you want to delete.
  447.  
  448. All message ports must also be deleted, but remember to first deallocate the 
  449. standard request block to which the message port is connected to, before you 
  450. delete the message port itself.
  451.  
  452. Synopsis
  453. :
  454.     
  455. DeletePort( msg_port );
  456.  
  457. msg_port:
  458.     
  459. (struct MsgPort *) Pointer to the MsgPort structure that should be deallocated.
  460.  
  461. Here is a short example:
  462.  
  463.   /* Pointer to the standard request block: */
  464.   struct IOStdReq *std_req;
  465.  
  466.   /* Pointer to the message (reply) port: */
  467.   struct MsgPort *msg_port;
  468.  
  469.  
  470.   /* 1. Create a message port so the device can         */
  471.   /*    communicate with us: (No name, normal priority) */
  472.   msg_port = CreatePort( NULL, 0 );
  473.   if( !msg_port )
  474.     clean_up( "ERROR! Could not create message port!" );
  475.  
  476.   /* 2. Allocate and initialize a new standard request block. */
  477.   /* It should use our new message port as reply port:        */
  478.   std_req = CreateStdIO( msg_port );
  479.   if( !std_req )
  480.     clean_up( "ERROR! Could not allocate the request block!" );
  481.  
  482.   ...
  483.  
  484.  
  485.   /* Deallocate standard request block: */
  486.   DeleteStdIO( std_req );
  487.  
  488.   /* Close message port: */
  489.   DeletePort( msg_port );
  490.  
  491. 1.2.3  IOEXTREQ
  492.  
  493. As if the extra IOStdReq structure would not be enough. There exist an even 
  494. larger request block, usually referred as the"extended request block". This 
  495. request structure always looks different depending on which device it is used 
  496. for. It always consists of a IORequest structure, or at least the same fields, 
  497. at the top, but the rest may vary. See the following chapters for more 
  498. information about this request block.
  499.  
  500. Since the size of this extended request block varies you can not use the 
  501. CreateStdIO() function to create and initialize it. Instead you have to use the 
  502. CreateExtIO() function which will allocate a request block of any specified 
  503. size. Except of allocating a larger request block it will do exactly the same 
  504. things as CreateStdIO(). (Remember that you first have to create a message 
  505. (reply) port as described above.)
  506.  
  507. Synopsis
  508. :
  509.     
  510. ext_req = CreateExtIO( msg_port, size );
  511.  
  512. ext_req:
  513.     
  514. (struct IORequest *) Pointer to the new extended request block, or NULL if the 
  515. request block could not be created.
  516.  
  517. msg_port:
  518.     
  519. (struct MsgPort *) Pointer to the message port the device should use to 
  520. communicate with you.
  521.  
  522. size:
  523.     
  524. (long) The number of bytes that should be allocated for the extended request 
  525. block. Use the function sizeof() to find the exact number of bytes needed.
  526.  
  527. When your program terminates all request block have to be deleted. Extended 
  528. request blocks are deleted with the special DeleteExtIO() function. Once the 
  529. request block has been deleted you should also delete the message (reply) port.
  530.  
  531. Synopsis
  532. :
  533.     
  534. DeleteExtIO( std_req, size );
  535.  
  536. std_req:
  537.     
  538. (struct IOStdReq *) Pointer to the extended request block you want to delete.
  539.  
  540. size:
  541.     
  542. (long) The size of the request block, in bytes.
  543.  
  544. Here is a short example: (In this example we will use an extended request block 
  545. designed for the "serial device".)
  546.  
  547.  
  548.   /* Pointer to the extended request block: */
  549.   struct IOExtSer *ext_req;
  550.  
  551.   /* Pointer to the message (reply) port: */
  552.   struct MsgPort *msg_port;
  553.  
  554.  
  555.   /* 1. Create a message port so the device can         */
  556.   /*    communicate with us: (No name, normal priority) */
  557.   msg_port = CreatePort( NULL, 0 );
  558.   if( !msg_port )
  559.     clean_up( "ERROR! Could not create message port!" );
  560.  
  561.   /* 2. Allocate and initialize a new extended request block. */
  562.   /* It should use our new message port as reply port, and    */
  563.   /* have the same size as an IOExtSer structure:             */
  564.   ext_req = CreateExtIO( msg_port, sizeof( struct IOExtSer ) );
  565.   if( !ext_req )
  566.     clean_up( "ERROR! Could not allocate the request block!" );
  567.  
  568.   ...
  569.  
  570.  
  571.   /* Deallocate extended request block: (Must be the same */
  572.   /* size as when allocated!)                             */
  573.   DeleteExtIO( ext_req, sizeof( struct IOExtSer ) );
  574.  
  575.   /* Close message port: */
  576.   DeletePort( msg_port );
  577.  
  578. 1.3  PREPARE THE DEVICE
  579.  
  580. Once you have created a request block you may "open" the device to which the 
  581. request block should be connected to. Note thatsome devices only need a 
  582. standard request block (or just a IORequest structure), but others may need 
  583. larger extended request blocks. See the following chapters for more 
  584. information.
  585.  
  586. To open a device simply use the OpenDevice() function.
  587.  
  588. Synopsis
  589. :
  590.     
  591. error = OpenDevice( name, unit, req, flags );
  592.  
  593. error:
  594.     
  595. (long) If OpenDevice() managed to open the device it returns 0, else an error 
  596. number is returned.
  597.  
  598. name:
  599.     
  600. (char *) Name of the device you want to open. See the following chapters for 
  601. more information.
  602.  
  603. unit:
  604.     
  605. (long) Which unit you want to open. Some devices does not have several units 
  606. and this field is then ignored.
  607.  
  608. req:
  609.     
  610. (struct IORequest *) Pointer to a request block. Note that some devices may 
  611. need a larger extended request blocks.
  612.  
  613. flags:
  614.     
  615. (long) Special flags may sometimes be used here, otherwise this field is 
  616. ignored. 
  617. As usual, everything that you open must be closed before the program may 
  618. terminated. To close a device simply call the CloseDevice() function. After you 
  619. have closed the device you should delete the request block and then the message 
  620. port. (Unless you want to reopen the device later on.)
  621.  
  622. Synopsis
  623. :
  624.     
  625. CloseDevice( req );
  626.  
  627. reg:
  628.     
  629. (struct IORequest *) Pointer to the device's request block.
  630.  
  631. 1.4  STANDARD EXEC COMMANDS
  632.  
  633. There exist eight standard commands which most devices understand. If the 
  634. device is already occupied by some other request, your request is queued on a 
  635. FIFO basis (First In First Out). For some devices it may happen that the other 
  636. requests are aborted and your started if your request has higher priority.
  637.  
  638. The eight "standard" commands are:
  639.  
  640. CMD_RESET:
  641.     
  642. This command will reset the device. All currently queued requests are removed 
  643. (aborted) and the device is reinitialized to the default values.
  644.  
  645. CMD_READ:
  646.     
  647. This command tells the device to start reading (collecting) data. The 
  648. "io_Length" field of the request block states how many bytes should be read, 
  649. and the "io_Actual" field will contain the number of bytes actually 
  650. transferred.
  651.  
  652. CMD_WRITE:
  653.     
  654. This command tells the device to start writing (sending) data. The "io_Length" 
  655. field of the request block states how many bytes should be written, and the 
  656. "io_Actual" field will contain the number of bytes actually transferred.
  657.  
  658. CMD_UPDATE:
  659.     
  660. This command will update a device in that sense that any values still in the 
  661. internal buffers are written out. Most times this is automatically done, but 
  662. this command may be needed when you are working with the "trackdisk device" for 
  663. example.
  664.  
  665. CMD_CLEAR:
  666.     
  667. This command will clear any internal memory buffers.
  668.  
  669. CMD_STOP:
  670.     
  671. When this command is sent to a device all communication to and from that device 
  672. will be temporarily halted. Use the "CMD_START" command to restart the 
  673. communication.
  674.  
  675. CMD_START:
  676.     
  677. This command tells the device to restart the communication which has 
  678. temporarily been halted by a "CMD_STOP" command. 
  679.  
  680. CMD_FLUSH:
  681.     
  682. If this command is sent to a device all currently queued requests will be 
  683. removed (aborted).
  684.  
  685. There exist several more commands, but those are device depending, and may 
  686. therefore only be used with a certain device. See the following chapters for 
  687. more information about these special device commands.
  688.  
  689. 1.5  SEND REQUESTS
  690.  
  691. Once you have initialized the request block and "opened" the desired device, 
  692. which is described in more details in the following chapters, you only have to 
  693. send the request to Exec and the rest will automatically be done for you. 
  694. (Isn't that nice! At last you do not need to worry about everything.)
  695. When you send requests to a device the commands can either be synchronous or 
  696. asynchronous. When you send synchronous commands your program is halted until 
  697. the request has been completed. With asynchronous commands your program will 
  698. continue to run while the device is executing your request.
  699.  
  700. 1.5.1  SYNCHRONOUS COMMANDS
  701.  
  702. Synchronous commands are very easy to handle. You simply send your request with 
  703. the DoIO() function. Your program will automatically be put to sleep while the 
  704. device is executing your command, and once it has finished your program will 
  705. wake up. Since your program is put to sleep ("task sleep") no processing time 
  706. is waisted while you are waiting for your request to be completed.
  707.  
  708. Synopsis
  709. :
  710.     
  711. error = DoIO( req );
  712.  
  713. error:
  714.     
  715. (long) DoIO() will return first when the request has been completed or 
  716. something has failed. If the request was successfully completed zero is 
  717. returned, else an error number is returned. What error number depends on which 
  718. device was used. See below for a complete list of standard error commands.
  719.  
  720. req:
  721.     
  722. (struct IORequest *) Pointer to the request you want to have executed.
  723.  
  724. 1.5.2  ASYNCHRONOUS COMMANDS
  725.  
  726. Asynchronous requests are a bit more complicated to handle. Since your program 
  727. will continue to run while your request isbeing executed you do not know when 
  728. it has been completed.  Asynchronous requests can be very useful when you do 
  729. not want to pause the program while waiting. (It would not be a very nice game 
  730. if it was halted each time the user did not move the joystick for example.)
  731.  
  732. You start asynchronous requests with help of the SendIO() function.
  733.  
  734. Synopsis
  735. :
  736.     
  737. SendIO( req )
  738.  
  739. req:
  740.     
  741. (struct IORequest *) Pointer to the request you want to have executed.
  742.  
  743. After you have issued an asynchronous request there exist three different ways 
  744. to find out when it has been completed.
  745.  
  746. 1.5.2.1  KEEP ON CHECKING THE REQUEST UNTIL IT HAS BEEN COMPLETED
  747.  
  748. You can use the CheckIO() function which will either return NULL if the request 
  749. has not been completed, or it will return a pointer to the request if it has 
  750. been completed.
  751.  
  752. Synopsis
  753. :
  754.     
  755. ptr = CheckIO( req );
  756.  
  757. ptr:
  758.     
  759. (long) CheckIO() will either return NULL if the request have not been completed 
  760. or it will return a pointer to the request block.
  761.  
  762. req:
  763.     
  764. (struct IORequest *) Pointer to the request you want to check.
  765.  
  766. When a request has been completed a message is sent to a specified reply port. 
  767. Note that CheckIO() will not remove the message at the reply port. This should 
  768. be done with the Remove() function.
  769.  
  770.  
  771. Synopsis
  772. :
  773.     
  774. Remove( node );
  775.  
  776. node:
  777.     
  778. (struct Node *) Pointer to the node that should be removed.
  779.  
  780. 1.5.2.2  WAIT FOR THE REQUEST TO BE COMPLETED
  781.  
  782. You can put your program to sleep and wait for the request to be completed, by 
  783. calling the WaitIO() function. WaitIO() willautomatically remove the message at 
  784. the reply port.
  785.  
  786. Synopsis
  787. :
  788.     
  789. error = WaitIO( req );
  790.  
  791. error:
  792.     
  793. (long) WaitIO() will return first when the request, that has previously been 
  794. sent, has been completed or something has failed. If the request was 
  795. successfully completed zero is returned, else an error number is returned. What 
  796. error number depends on which device was used.
  797.  
  798. req:
  799.     
  800. (struct IORequest *) Pointer to the request you want to wait for to be 
  801. completed. Note that the request must have already been sent to the device by 
  802. either a SendIO() or BeginIO() function call. If the request has already been 
  803. completed WaitIO() will immediately return.
  804.  
  805. 1.5.2.4  WAIT FOR A REPLY MESSAGE
  806.  
  807. Since a message is sent to the request block's reply port each time the request 
  808. has been completed you can simply wait for amessage to arrive. The simplest way 
  809. is to use the WaitPort() function which will put your program to sleep while 
  810. waiting for a message to arrive at the specified port. Note that it will not 
  811. remove the message.
  812.  
  813. Synopsis
  814. :
  815.     
  816. msg = WaitPort( msg_port );
  817.  
  818. msg:
  819.     
  820. (struct Message *) Pointer to the first message which has arrived. Remember 
  821. that this function will not remove the message from the queue. Use the Remove() 
  822. function to do this.
  823.  
  824. msg_port:
  825.     
  826. (struct MsgPort *) Pointer to the message port which you want to monitor. Your 
  827. program will be put to sleep while waiting, and if no message arrive it will 
  828. never wake up, so be careful.
  829.  
  830. Instead of just waiting for one request to be completed you can wait fore 
  831. several ones at the same time. This is extremely useful if your program is 
  832. handling a lot of requests simultaneously. Now we need to use the multipurpose 
  833. Wait() function which is described in the "System" manual, chapter "Messages".
  834.  
  835. Synopsis
  836. :
  837.     
  838. rsig = Wait( wsig )
  839.  
  840. rsig:
  841.     
  842. (ULONG) When one of the specified signal bits was received, the task is woken 
  843. up and Wait() returns the signal bit value that woke it up.
  844.  
  845. wsig:
  846.     
  847. (ULONG) The signals we are waiting for. Wait() will put the task to sleep, and 
  848. it will only wake up if one of the specified signal bit values is received.
  849.  
  850.  
  851. 1.5.4  QUICK I/O
  852.  
  853. Although the system with sending requests and messages is very fast it may 
  854. sometimes not be fast enough. If you want to handle a lot of data extremely 
  855. fast you can try to use the "Quick mode". When you are using this quick mode 
  856. Exec tries to skip all routines that are not essential. No messages are sent, 
  857. and there is no checking etc...
  858.  
  859. The quick mode should only be used when really needed, and it can not always be 
  860. used. When you want to try to use the quick mode set the IOF_QUICK flag in the 
  861. "io_Flags" field of the request block. To pass the request to Exec you should 
  862. not use the DoIO() or SendIO() functions, but instead the low level function 
  863. BeginIO().
  864.  
  865. Synopsis
  866. :
  867.     
  868. BeginIO( req )
  869.  
  870. req:
  871.     
  872. (struct IORequest *) Pointer to the request you want to have executed. When you 
  873. want to use the quick mode you have to use this low level function rather than 
  874. SendIO() and DoIO().
  875.  
  876. If we were allowed to use the quick mode the request will be synchronous. (Your 
  877. program is put to sleep since all computer time is needed to run this fast 
  878. quick mode.)
  879.  
  880. If we were not allowed to use the quick mode the request will be asynchronous, 
  881. and is then handled exactly the same as if the request had been sent by a 
  882. SendIO() call.
  883.  
  884. After you have posted your quick request you must check if you were allowed to 
  885. use this fast mode or not. If not you have to wait for the request to be 
  886. completed and remove the reply message yourself. To check if you were allowed 
  887. quick mode or not simply look at the "io_Flags" field of the request block to 
  888. see if the flag IOF_QUICK is still there or not. If the flag is still set the 
  889. request was using the quick mode, and you do not need to do anything more. If 
  890. the flag is not there any more, you were not allowed quick mode, and thus you 
  891. have to wait for the asynchronous request to be completed and remove the 
  892. message yourself.
  893.  
  894.   /* Initialize the request block as normal. */
  895.   /* The pointer to it is called "ioreq".    */
  896.   /* ...                                     */
  897.  
  898.   /* Try to use the "quick" mode: */
  899.   ioreq->IOSer.io_Flags = IOF_QUICK;
  900.  
  901.   /* Do the request: */
  902.   BeginIO( ioreq );
  903.  
  904.   /* Check if the flag IOF_QUICK is still there or not: */
  905.   if( (ioreq->IOSer.io_Flags & IOF_QUICK) )
  906.   {
  907.     /* OK! The request was using quick mode, which means    */
  908.     /* that our task was put to sleep while the request was */
  909.     /* executed, and the request has now been completed.    */
  910.     /* Since we are using quick mode there are no message   */
  911.     /* that should be removed.                              */
  912.   }
  913.   else
  914.   {
  915.     /* Too bad, we were not allowed to use the quick mode. */
  916.     /* The request should now be treated as if it was      */
  917.     /* started with a SendIO() function call. This request */
  918.     /* is asynchronous - request returns immediately.      */
  919.   
  920.     /* Wait for the request to be completed. WaitIO() will */
  921.     /* also automatically remove the message at the reply  */
  922.     /* port.                                               */
  923.     WaitIO( ioreq );
  924.   }
  925.  
  926. 1.5.3  ABORT REQUESTS
  927.  
  928. You can abort a previously started request by calling the AbortIO() function. 
  929. Of course, only asynchronous commands can be aborted, and if the request has 
  930. already been completed it can not be aborted any more.
  931.  
  932. Synopsis
  933. :
  934.     
  935. AbortIO( req )
  936.  
  937. req:
  938.     
  939. (struct IORequest *) Pointer to the request you want to abort.
  940.  
  941. 1.6  ERRORS
  942.  
  943. No one is perfect, and even the devices may sometimes not be able to do what 
  944. you tell them to do (usually because you have done something wrong). If 
  945. something goes fails an error number is returned. Each device has it own set of 
  946. unique error codes which are described in the following chapters. There exist, 
  947. however, four error codes that are sent by Exec. (They are all defined in the 
  948. header file "exec/errors.h")
  949.  
  950. IOERR_OPENFAIL:
  951.     
  952. The device (unit) could not be opened. If you receive this message there is 
  953. normally some other program currently using the device with exclusive mode.
  954.  
  955. IOERR_ABORTED:
  956.     
  957. When you abort a previously started request by calling the AbortIO() function, 
  958. the io_Error filed of that request is set to IOERR_ABORTED. If you find a 
  959. request block with this flag set, you know that it has been aborted.
  960.  
  961. IOERR_NOCMD:
  962.     
  963. You tried to use a command that is not supported by that device.
  964.  
  965. IOERR_BADLENGTH:
  966.     
  967. The length of the request was not valid.
  968.  
  969. 1.7  DEVICES
  970.  
  971. There exist a lot of different devices each specialized to do something unique. 
  972. Some devices are so called "low level devices" and are handling a lot of fresh 
  973. untranslated raw data which will be modified and later used by other so called 
  974. "high level devices".
  975.  
  976. In total there exist 12 devices (14 with the new V2.05 operating system) which 
  977. all will be described in a chapter each. Although the devices are so different, 
  978. they all are handled in much the same manner - allocate a message port and a 
  979. request block, open the device and start to send your request, finally close 
  980. everything and quit.
  981.  
  982. Five devices are not covered in this edition, but will soon be added. All 
  983. registered members will receive more information about this as soon as the 
  984. chapters have been completed.
  985.  
  986. 1.7.1  Timer Device
  987.  
  988. The Timer Device can be used if you want to receive a message after a specified 
  989. time limit. This is one of the so called "low level devices", and is used by 
  990. many other devices. Although the timer device does not sound very interesting, 
  991. it can really be useful.
  992.  
  993. 1.7.2  Gameport Device
  994.  
  995. The Gameport Device is a smart little device which reads data from the two 
  996. gameport connectors on the Amiga. The device can handle both normal Joystick as 
  997. well as Mouse movements. Even light-pens and other input devices may be 
  998. connected and monitored by this device. These special input devices must 
  999. however have its own drivers which translates the signals to or the gameport 
  1000. device understandable form.
  1001.  
  1002. 1.7.3  Audio Device
  1003.  
  1004. The Audio Device is used to produce sound. It monitors the four hardware sound 
  1005. channels, and can therefore play the sound in stereo. Everything from a simple 
  1006. beep to complete songs may be played. The audio device can both produce sounds 
  1007. itself or directly play sampled (digitized) sound effects.
  1008.  
  1009. Sound is an important part of our life and should therefore in my opinion also 
  1010. be used a lot in computer programs. Sound can be used in most situations, from 
  1011. emergency beeps to entertaining melodies.
  1012.  
  1013. 1.7.4  Narrator Device
  1014.  
  1015. The Amiga is not only extremely good at producing high quality sound. with help 
  1016. of the Narrator Device it can also speak almost as a human. Although it is not 
  1017. a Frank Sinatra, it is possible to understand what it is saying, and this can 
  1018. be used in many situations. Used with care, it is possible to produce very good 
  1019. sound.
  1020.  
  1021. Sadly, somehow many persons do not like this unique feature of the Amiga, and 
  1022. very few programs support the narrator device. I believe that artificial speech 
  1023. can be used in many situations, and can really be handy for the user. Most 
  1024. "professional" programs would really benefit from using the narrator device.
  1025.  
  1026. 1.7.5  Trackdisk Device
  1027.  
  1028. With help of the Trackdisk Device you can directly access the disk drivers. 
  1029. Data can not only be transferred to and from the disks, but you may even format 
  1030. specific sectors of a disk and do a lot of other low level stuff.
  1031.  
  1032. 1.7.6  Serial Device   
  1033.  
  1034. The Serial Device is used to transfer data from and to the serial port. It can 
  1035. be used at different speeds (bauds) and use seven or eight bit characters. The 
  1036. device also support parity (error) checking.
  1037.  
  1038. 1.7.7  Parallel Device
  1039.  
  1040. The Parallel device is used to transfer data from and to the parallel port. The 
  1041. parallel device is very useful when you want very fast communications, and is 
  1042. therefore often used by sound samplers and video digitizers. However, the most 
  1043. commonly used external device is without question a printer, but you should 
  1044. then use the printer device which has specially been designed for this purpose.
  1045.  
  1046. 1.7.8  Printer Device
  1047.  
  1048. The printer device can use both the serial and parallel device depending on to 
  1049. which port the printer is connected to. With the printer device you can not 
  1050. only print normal text, specialprinter commands may be sent and you can even 
  1051. print graphics with it.
  1052.  
  1053. The printer device uses the preferences settings to find out to which port the 
  1054. printer is connected, what printer it is, and other special settings like 
  1055. margins, paper length etc...
  1056.  
  1057. 1.7.9  Keyboard Device
  1058.  
  1059. The Keyboard Device is a "low level device" which is used by the "Input 
  1060. Device". The keyboard device collects untranslated (raw) key codes. This device 
  1061. is very fast and uses very little computer time.
  1062.  
  1063. This device will be covered in the next version of the manual. All registered 
  1064. members will receive more information as soon as the next version has been 
  1065. completed.
  1066.  
  1067. 1.7.10  Input Device
  1068.  
  1069. The Input Device is a so called "high level device" which receives information 
  1070. from the timer, keyboard and gameport devices, and puts all this information 
  1071. into one single input stream. Very handy and useful device.
  1072.  
  1073. This device will be covered in the next version of the manual. All registered 
  1074. members will receive more information as soon as the next version has been 
  1075. completed.
  1076.  
  1077. 1.7.11  Console Device
  1078.  
  1079. The Console Device enables you to handle input and output as an intelligent 
  1080. terminal for ASCII characters. Works very close with Intuition, and is also a 
  1081. very handy and useful device.
  1082.  
  1083. This device will be covered in the next version of the manual. All registered 
  1084. members will receive more information as soon as the next version has been 
  1085. completed.
  1086.  
  1087. 1.7.12  Clipboard Device
  1088.  
  1089. With the Clipboard Device data can be cut, pasted and copiedbetween different 
  1090. programs. Several clips may be used simultaneously, and the device supports the 
  1091. IFF standard.
  1092.  
  1093. This device will be covered in the next version of the manual. All registered 
  1094. members will receive more information as soon as the next version has been 
  1095. completed.
  1096.  
  1097. 1.7.13  SCSI AND PCMCIA DEVICE
  1098.  
  1099. There exist two new devices. The SCSI device is only available with the new 
  1100. V2.0 operating system, and the PCMCIA device exist for the moment only on Amiga 
  1101. 600.
  1102.  
  1103. These device will be covered in the next version of the manual. All registered 
  1104. members will receive more information as soon as the next version has been 
  1105. completed.
  1106.  
  1107.  
  1108. TIMER DEVICE
  1109.  
  1110. 2.1  INTRODUCTION
  1111.  
  1112. The timer device may not sound very exciting but it can be extremely useful. 
  1113. Many so called "high level" devices are using the timer device, and very often 
  1114. you need to use it yourself. It is not very accurate since other tasks running 
  1115. at the sametime may delay the time reports, but is usually good enough, and in 
  1116. the long run it is very accurate.
  1117.  
  1118. The timer device is a very simple device. It's main task is to send messages to 
  1119. all its users after specified time periods. The timer device can also compare 
  1120. different time periods, and add as well as subtract one time period with 
  1121. another. No thrills, but useful stuff.
  1122.  
  1123. 2.2  TIMER
  1124.  
  1125. The timer device's main task is to send time reports to all its users. It is 
  1126. controlled like all other devices by sending request blocks, and is therefore 
  1127. easy to handle. See previous chapter "Devices" for more information about 
  1128. devices and request blocks.
  1129.  
  1130. 2.2.1  TIME REQUEST
  1131.  
  1132. The timer device's request block is an "extended" request block and is defined 
  1133. in header file "devices/timer.h": 
  1134.  
  1135. struct timerequest
  1136. {
  1137.   struct IORequest tr_node;
  1138.   struct timeval   tr_time;
  1139. };
  1140.  
  1141. tr_node:
  1142.     
  1143. As with all request blocks, the top part consists of an IORequest structure 
  1144. which is used by Exec to handle the requests. While using the timer device you 
  1145. do not need to use this structure (well, at least not very often). Simply let 
  1146. the system take care of it.
  1147.  
  1148. tr_time:
  1149.     
  1150. The second part of the request block consists of a timeval structure: (Also 
  1151. defined in the header file "devices/timer.h".)
  1152.  
  1153.     
  1154. struct timeval
  1155.     
  1156. {
  1157.     
  1158.     
  1159. ULONG tv_secs;
  1160.     
  1161.     
  1162. ULONG tv_micro;
  1163.     
  1164. };
  1165.          
  1166.     
  1167. tv_secs:
  1168.     
  1169. The number of seconds you want to wait before the time request should be 
  1170. completed.
  1171.  
  1172.     
  1173. tv_micro:
  1174.     
  1175. The number of microseconds. Note that the timer device is not very accurate. 
  1176. Your requests can be delayed by other programs running at the same time, but in 
  1177. the long run the timer device is accurate enough.
  1178.  
  1179. As with all devices you have to connect a message port to the request block 
  1180. with which the timer device can talk to you. Create a message (reply) port by 
  1181. calling the CreatePort() function. Normally you should not use any name (the 
  1182. message port should not be made public) and priority set to 0 (normal 
  1183. priority).
  1184.  
  1185. Synopsis
  1186. :
  1187.     
  1188. msg_port = CreatePort( name, pri );
  1189.   
  1190. msg_port:
  1191.     
  1192. (struct MsgPort *) Pointer to the new MsgPort structure, or NULL if something 
  1193. went wrong.
  1194.  
  1195. name:
  1196.     
  1197. (char *) Pointer to a string containing the name of the message port, or NULL. 
  1198. When working with devices you normally do not need to use any name.
  1199.   
  1200. pri:
  1201.     
  1202. (BYTE) This message port's priority. Usually set to 0 (normal priority).
  1203.  
  1204. Once you have successfully opened a message port you may allocate and 
  1205. initialize the request block. Since the request block is "extended" (not the 
  1206. same size as the "standard" - IOStdReq structures) you need to use the 
  1207. CreateExtIO() function to allocate and initialize the request block.(Do not use 
  1208. the CreateStdIO() function!)
  1209.  
  1210. Synopsis
  1211. :
  1212.     
  1213. ext_req = CreateExtIO( msg_port, size );
  1214.  
  1215. ext_req:
  1216.     
  1217. (struct IORequest *) Pointer to the new extended request block, or NULL if the 
  1218. request block could not be created.
  1219.  
  1220. msg_port:
  1221.     
  1222. (struct MsgPort *) Pointer to the message port the CreatePort() function 
  1223. returned. The timer device will use it to send messages to you.
  1224.  
  1225. size:
  1226.     
  1227. (long) The number of bytes that should be allocated for the extended request 
  1228. block. Use the function sizeof() to find the exact number of bytes needed. 
  1229. Example: sizeof( struct timerequest ).
  1230.  
  1231. Here is an example:
  1232.  
  1233.   /* The reply port: */
  1234.   struct MsgPort *replymp;
  1235.  
  1236.   /* The timer request block: */
  1237.   struct timerequest *timer_req;
  1238.  
  1239.   ...
  1240.  
  1241.  
  1242.   /* Get a reply port: (No name, normal priority) */
  1243.   replymp = (struct MsgPort *)
  1244.     CreatePort( NULL, 0 );
  1245.   if( !replymp )
  1246.     clean_up( "Could not create the reply port!" );
  1247.  
  1248.   /* Allocate and preinitialize a request block: */
  1249.   timer_req = (struct timerequest *)
  1250.     CreateExtIO( replymp, sizeof(struct timerequest) );
  1251.   if( !timer_req )
  1252.     clean_up( "Could not create the request block!" );
  1253.  
  1254. 2.2.2  OPEN THE TIMER DEVICE
  1255.  
  1256. Once you have successfully created a request block you can "open" the timer 
  1257. device. You should, however, first decide how accurate you want the timer 
  1258. device to be. The timer device can work in two different modes:
  1259.  
  1260. 1. The timer device can use the video beam to control its own timer. This will 
  1261. make the timer very stable over long time periods, but is not very accurate for 
  1262. short intervals (less than one second).
  1263.      
  1264.     
  1265. This mode is easy for the timer device to handle and thus little computer time 
  1266. is used. This mode is called "vertical blank timer" and the flag is 
  1267. "UNIT_VBLANK".
  1268.  
  1269. 2. If you want to use very short time periods (less than one second) it can 
  1270. sometimes be necessary to use the special CIA chips for the timer. The CIA 
  1271. chips has a much higher "resolution" than the video beam, thus it will result 
  1272. in very accurate time periods for short intervals. This mode is more 
  1273. complicated than the vertical blank timer, and more computer time is therefore 
  1274. used. This mode is called "micro hertz timer" and the flag is "UNIT_MICROHZ"
  1275.  
  1276. The general rule is that time periods longer than one second should use the 
  1277. vertical blank timer. Else, if really high "resolution" is needed, the micro 
  1278. hertz timer should be used.
  1279.  
  1280. Once you have decided which mode should be used you can open the timer device. 
  1281. No frills here, simply use the OpenDevice() function.
  1282.  
  1283. Synopsis
  1284. :
  1285.     
  1286. error = OpenDevice( name, unit, req, flags );
  1287.  
  1288. error:
  1289.     
  1290. (long) If OpenDevice() managed to open the timer device it returns 0, else an 
  1291. error number is returned.
  1292.  
  1293. name:
  1294.     
  1295. (char *) Name of the device you want to open. The name for the timer device has 
  1296. been defined as "TIMERNAME" (header file "devices/timer.h").
  1297.  
  1298. unit:
  1299.     
  1300. (long) Which timer mode you want the device to use. Set this field to either 
  1301. "UNIT_VBLANK" or "MICROHZ".
  1302.  
  1303. req:
  1304.     
  1305. (struct IORequest *) Pointer to a previously initialized request block.
  1306.  
  1307. flags:
  1308.     
  1309. (long) Special flags which are ignored by the timer device.
  1310.  
  1311. Here is an example:
  1312.  
  1313.   /* Open the Timer Device: ("vertical blank timer") */
  1314.   error = OpenDevice( TIMERNAME, UNIT_VBLANK, timer_req ,0 );
  1315.   if( error )
  1316.     clean_up( "Could not open the Timer Device!" );
  1317.  
  1318. 2.2.3  SET TIME REQUEST
  1319.  
  1320. When the timer device has been opened you can start to send your time requests. 
  1321. You specify the number of seconds and microseconds that should pass before a 
  1322. message will be sent by initializing the timeval structure. Note that the time 
  1323. you set is the minimum time. When the exact time has elapsed it may happen that 
  1324. some other program is for the moment occupying the processor, and thus the time 
  1325. message will be delayed for some microseconds.
  1326. You must also tell the device that you are sending a time request. You do it by 
  1327. setting the flag "TR_ADDREQUEST" in the "io_Command" field.
  1328.  
  1329. The request can now be sent to the device. Either use the DoIO() function if 
  1330. you want to wait for the request to be completed, or use the SendIO() function 
  1331. if you want your program to continue to run while the request is executed. See 
  1332. the previous chapter for a complete explanation on when and how the DoIO() and 
  1333. SendIO() functions should be used.
  1334.  
  1335. Here is an example:
  1336.  
  1337.   /* Set time: (5.25 seconds)*/
  1338.   timer_req->tr_time.tv_secs = 5;
  1339.   timer_req->tr_time.tv_micro = 250000;
  1340.  
  1341.   /* We want to add a time request: */
  1342.   timer_req->tr_node.io_Command = TR_ADDREQUEST;
  1343.  
  1344.   /* Do our request and return when done:     */
  1345.   /* (Our task is put to sleep for 5.25 sec.) */
  1346.   DoIO( timer_req );
  1347.  
  1348. If you want to send multiple requests you have to use several request blocks 
  1349. which each has been properly initialized. You must then also use the function 
  1350. SendIO() and not DoIO() since your program will else be put to sleep directly 
  1351. after the first request has been sent. See example 2.
  1352.  
  1353. Each time you send a request to the timer device all values in the timeval 
  1354. structure are destroyed. You must therefore always reinitialize these fields 
  1355. before you may repost the request to device.
  1356.  
  1357. 2.2.4  CLEAN UP
  1358.  
  1359. As always you must remember to clean up after you. All allocated request blocks 
  1360. must be removed, all opened message ports closed as well as all devices.
  1361.  
  1362. Fist you should close the timer device. Note that all requests you have posted 
  1363. to that device must either have been completed or aborted before you may close 
  1364. the device! All devices are closed by the special CloseDevice() function. 
  1365.  
  1366. Synopsis
  1367. :
  1368.     
  1369. CloseDevice( req );
  1370.  
  1371. reg:
  1372.     
  1373. (struct IORequest *) Pointer to the timer device's own request block. (The same 
  1374. request block which was used when you opened the timer device.)
  1375.  
  1376. The request blocks are deleted by calling the DeleteExtIO() function. Note that 
  1377. the request must have been completed or aborted before you may delete it! Note 
  1378. also that you can not use the DeleteStdIO() function since the timer device is 
  1379. using extended request blocks.
  1380.  
  1381. Synopsis
  1382. :
  1383.     
  1384. DeleteExtIO( std_req, size );
  1385.  
  1386. std_req:
  1387.     
  1388. (struct IOStdReq *) Pointer to the request block you want to delete.
  1389.  
  1390. size:
  1391.     
  1392. (long) The size of the request block, in bytes. Use the function sizeof() to 
  1393. get the correct number of bytes. Example: sizeof( struct timerequest ).
  1394.  
  1395. Finally you should close all message ports by calling the DeletePort() 
  1396. function. All messages must have been removed before you may close the message 
  1397. port.
  1398.  
  1399. Synopsis
  1400. :
  1401.     
  1402. DeletePort( msg_port );
  1403.  
  1404. msg_port:
  1405.     
  1406. (struct MsgPort *) Pointer to the MsgPort structure that should be deleted.
  1407.  
  1408. Here is a short example:
  1409.  
  1410.   /* Close the Timer Device: */
  1411.   CloseDevice( timer_req );
  1412.  
  1413.   /* Delete the request block: */
  1414.   DeleteExtIO( timer_req, sizeof( struct timerequest) );
  1415.  
  1416.   /* Remove the message port: */
  1417.   DeletePort( replymp );
  1418.  
  1419. 2.2.5  EXAMPLE
  1420.  
  1421. Here is a complete example on how to use the timer device. It will open a 
  1422. message port, create a request block, open the timerdevice, and finally put 
  1423. itself to sleep for 10 seconds. When the time has elapsed everything is 
  1424. returned and the program terminates. (Hmmm, very similar to example 1.)
  1425.  
  1426. #include <exec/types.h>    /* STRPTR         */
  1427. #include <exec/ports.h>    /* struct Message */
  1428. #include <exec/memory.h>   /* MEMF_PUBLIC    */
  1429. #include <devices/timer.h> /* TIMERNAME */
  1430.  
  1431. /* The reply port: */
  1432. struct MsgPort *replymp;
  1433.  
  1434. /* The timer request block: */
  1435. struct timerequest *timer_req;
  1436.  
  1437. /* When the Timer Device is open this variable is TRUE: */
  1438. BOOL not_opened = TRUE;
  1439.  
  1440. /* Declare our functions: */
  1441. void clean_up();
  1442. void main();
  1443.  
  1444. void main()
  1445. {
  1446.   /* 1. Get a reply port: */
  1447.   replymp = (struct MsgPort *)
  1448.     CreatePort( NULL, 0 );
  1449.   if( !replymp )
  1450.     clean_up( "Could not create the reply port!" );
  1451.  
  1452.   /* 2. Get an extended request block: */
  1453.   timer_req = (struct timerequest *)
  1454.     CreateExtIO( replymp, sizeof( struct timerequest) );
  1455.   if( !timer_req )
  1456.     clean_up( "Could not create the IO!" );
  1457.  
  1458.   /* 3. Open the Timer Device: */
  1459.   not_opened = OpenDevice( TIMERNAME, UNIT_VBLANK, timer_req ,0 );
  1460.   if( not_opened )
  1461.     clean_up( "Could not open the Timer Device!" );
  1462.  
  1463.   /* 4. Set our request: */
  1464.   timer_req->tr_node.io_Command = TR_ADDREQUEST; /* Add a request.   */
  1465.   timer_req->tr_time.tv_secs = 10;               /* 10 seconds.      */
  1466.   timer_req->tr_time.tv_micro = 0;               /* 0 micro seconds. */
  1467.  
  1468.   /* 5. Do our request: (Wait 10 seconds) */
  1469.   printf( "Good night...\n" );
  1470.   DoIO( timer_req );
  1471.   printf( "Good morning!\n" );
  1472.  
  1473.   /* Clean up and quit: */
  1474.   clean_up( "The End!" );
  1475. }
  1476.  
  1477. void clean_up( text )
  1478. STRPTR text;
  1479. {
  1480.   /* Close the Timer Device: */
  1481.   if( !not_opened )
  1482.     CloseDevice( timer_req );
  1483.  
  1484.   /* Delete the extended request block: */
  1485.   if( timer_req )
  1486.     DeleteExtIO( timer_req, sizeof( struct timerequest) );
  1487.  
  1488.   /* Remove the message port: */
  1489.   if( replymp )
  1490.     DeletePort( replymp);
  1491.  
  1492.   /* Print the last message: */
  1493.   printf( "%s\n", text );
  1494.  
  1495.   /* Quit: */
  1496.   exit( 0 );
  1497. }
  1498.  
  1499. 2.3  SYSTEM TIME
  1500.  
  1501. The timer device can also be used to get the current system time. It should be 
  1502. noted that this system time has nothing to do with the Amiga's internal clock. 
  1503. The system time is simply a value which is initialized to the date when the 
  1504. boot disk was last modified. The value is relative to 1 January 1978, time 
  1505. 00:00:00.
  1506.  
  1507. Although it may not be a "correct" time value it can be useful. For example, if 
  1508. you want to adjust several time periods with each other it can be handy to have 
  1509. an external timer like the system time.
  1510.  
  1511. With help of the timer device you can both get the current system time as well 
  1512. as set it to a new value.
  1513.  
  1514. 2.3.1  GET SYSTEM TIME
  1515.  
  1516. To get the current system time you set the flag "TR_GETSYSTIME" in the 
  1517. io_Command field of the request block and send therequest block to the timer 
  1518. device. When the timer device has completed the request it is returned and you 
  1519. may now examine the timeval structure where the current system time has been 
  1520. stored.
  1521.  
  1522. Each time you get the system time you will receive a value which is different 
  1523. from previous calls (it is an unique value).
  1524.  
  1525. Here is a short example:
  1526.  
  1527.   /* Get the current system time: */
  1528.   timer_req->tr_node.io_Command = TR_GETSYSTIME;
  1529.  
  1530.   /* Do the request: */
  1531.   DoIO( timer_req );
  1532.  
  1533.   /* Print the current system time: */
  1534.   printf( "The current system time is:\n" );
  1535.   printf( "Seconds:      %d\n", timer_req->tr_time.tv_secs );
  1536.   printf( "Microseconds: %d\n", timer_req->tr_time.tv_micro );
  1537.  
  1538. 2.3.2  SET SYSTEM TIME
  1539.  
  1540. To set the system time to a new value simply set the "TR_SETSYSTIME" flag in 
  1541. the io_Command field of the request block, set the new time in the timeval 
  1542. structure, and finally send the request block to the timer device.
  1543.  
  1544. You may only set values that are greater than the current time. The timer 
  1545. device or other programs may be confused if the time suddenly went backwards.
  1546.  
  1547. Here is a short example:
  1548.  
  1549.   /* Add two hours and 0.4 seconds: */
  1550.   tr->tr_time.tv_secs += 60 * 60 * 2;
  1551.   tr->tr_time.tv_micro += 400000;
  1552.  
  1553.   /* Set the new system time: */
  1554.   tr->tr_node.io_Command = TR_SETSYSTIME;
  1555.  
  1556.   /* Do the request: */
  1557.   DoIO( tr );
  1558.  
  1559. 2.4  SPECIAL TIME FUNCTIONS
  1560.  
  1561. The timer device can also be used to compare different time reports and to add 
  1562. or subtract time values. To do these things the timer device contain three 
  1563. functions - CmpTime(), AddTime() and Subime(). Since these features are 
  1564. accessed as functions rather than using request blocks you have to prepare the 
  1565. timer device a little before you may use these functions.
  1566.  
  1567. The timer device is using a global pointer which is always called "TimerBase". 
  1568. This global pointer is used to find the starting address of the three special 
  1569. timer functions. Before you may use these functions you must therefore declare 
  1570. and initialize the TimerBase pointer. To initialize it simply use any request 
  1571. block which is connected to the timer device. In the "io_Device" field of the 
  1572. request block you will find the current address value of the timer device.
  1573.  
  1574. Here is a short example:
  1575.  
  1576.   /* Declare the global timer pointer: */
  1577.   struct Device *TimerBase;
  1578.  
  1579.   /* ... create request blocks, open timer device etc ... */
  1580.  
  1581.   /* Get the global pointer to the timer device: */
  1582.   TimerBase = timer_req->tr_node.io_Device;
  1583.  
  1584. Once you have found the TimerBase pointer you may start to use the timer 
  1585. functions. Their primary usage is to coordinate different time requests with 
  1586. each other.
  1587.  
  1588. 2.4.1  COMPARE TIMES
  1589.  
  1590. The CmpTime() function is used to compare two timeval structures and returns 0 
  1591. if they were identical, -1 if the first timeval structure was greater than the 
  1592. second, and 1 if the opposite (the second timeval structure was greater than 
  1593. the first).
  1594.  
  1595. Synopsis
  1596. :
  1597.     
  1598. dif = CmpTime( time1, time2 );
  1599.  
  1600. dif:
  1601.     
  1602. (long) If the two time values were identical 0 is returned. If the first time 
  1603. value was greater than the second time value -1 is returned, and if the 
  1604. opposite (the second time value is greater than the first time value) 1 is 
  1605. returned.
  1606.  
  1607. time1:
  1608.     
  1609. (struct timeval *) Pointer to the first timeval structure.
  1610.  
  1611. time1:
  1612.     
  1613. (struct timeval *) Pointer to the second timeval structure.
  1614.  
  1615. 2.4.2  ADD TIME
  1616.  
  1617. The AddTime() function is used to add the time in one timeval structure with 
  1618. the time in another timeval structure.
  1619.  
  1620. Synopsis
  1621. :
  1622.     
  1623. AddTime( tiem1, time2 );
  1624.  
  1625. time1:
  1626.     
  1627. (struct timeval *) Pointer to the first timeval structure. The value of the 
  1628. second timeval structure will be added with this timeval structure, and the 
  1629. result stored here.
  1630.  
  1631. time2:
  1632.     
  1633. (struct timeval *) Pointer to the second timeval structure.
  1634.  
  1635. 2.4.3  SUBTRACT TIME
  1636.  
  1637. The SubTime() function is used to subtract the time in one timeval structure 
  1638. with the time in another timeval structure.
  1639.  
  1640.  
  1641. Synopsos:
  1642.     
  1643. SubTime( tiem1, time2 );
  1644.  
  1645. time1:
  1646.     
  1647. (struct timeval *) Pointer to the first timeval structure. The value of the 
  1648. second timeval structure will be subtracted with this timeval structure, and 
  1649. the result stored here.
  1650.  
  1651. time2:
  1652.     
  1653. (struct timeval *) Pointer to the second timeval structure.
  1654.  
  1655. 2.4.5  EXAMPLE
  1656.  
  1657. This short example demonstrates how to use the timer device's own functions. 
  1658. ("tr1", "tr2" and "tr3" are three alreadyinitialized requst blocks.)
  1659.  
  1660.   /* Set the times: */
  1661.   /* Request 1: */
  1662.   tr1->tr_time.tv_secs = 10;
  1663.   tr1->tr_time.tv_micro = 0;
  1664.   /* Request 2: */
  1665.   tr2->tr_time.tv_secs =  7;
  1666.   tr2->tr_time.tv_micro = 0;
  1667.   /* Request 3: */
  1668.   tr3->tr_time.tv_secs =  5;
  1669.   tr3->tr_time.tv_micro = 0;
  1670.  
  1671.   /* Get a pointer to the timer device: */
  1672.   TimerBase = tr1->tr_node.io_Device;
  1673.  
  1674.   /* Compare the first two time val structures: */
  1675.   printf( "Compare time1 - time2: %d\n",
  1676.     CmpTime( &(tr1->tr_time), &(tr2->tr_time) ) );
  1677.  
  1678.   /* Add the time in the third timeval structure to   */
  1679.   /* the second timeval structure. The second timeval */
  1680.   /* structure should now contain the time 12 sec.    */
  1681.   AddTime( &(tr2->tr_time), &(tr3->tr_time) );
  1682.  
  1683.   /* Subtract the time in the third timeval structure with  */
  1684.   /* the second timeval structure. The second timeval       */
  1685.   /* structure should now again contain the time 7 seconds. */
  1686.   SubTime( &(tr2->tr_time), &(tr3->tr_time) );
  1687.  
  1688.  
  1689. Note that we have to use pointers to the timeval structures
  1690. as arguments [&(tr1->tr_time]. The parentheses around the
  1691. expression are actually unnecessary since the "->" operator
  1692. already has higher priority than the "&" operator. However,
  1693. this makes it easier to read.
  1694.  
  1695. 2.5  FUNCTIONS
  1696.  
  1697. The functions DoIO(), SendIO(), WaitIO(), AbortIO() etc... have already been 
  1698. defined in the previous chapter, and I will therefore not repeat all this 
  1699. information here.
  1700.  
  1701. There exist three functions which are supported by the timer device. Before you 
  1702. may use them you have to initialize a global pointer named "TimerBase". (See 
  1703. above for more information.)
  1704.  
  1705. CmpTime()
  1706.  
  1707. The CmpTime() function is used to compare two timeval structures and returns 0 
  1708. if they were identical, -1 if the first timeval structure was greater than the 
  1709. second, and 1 if the opposite (the second timeval structure was greater than 
  1710. the first).
  1711.  
  1712. Synopsis:
  1713.     
  1714. dif = CmpTime( time1, time2 );
  1715.  
  1716. dif:
  1717.     
  1718. (long) If the two time values were identical 0 is returned. If the first time 
  1719. value was greater than the second time value -1 is returned, and if the 
  1720. opposite (the second time value is greater than the first time value) 1 is 
  1721. returned.
  1722.  
  1723. time1:
  1724.     
  1725. (struct timeval *) Pointer to the first timeval structure.
  1726.  
  1727. time2:
  1728.     
  1729. (struct timeval *) Pointer to the second timeval structure.
  1730.  
  1731. AddTime()
  1732.  
  1733. The AddTime() function is used to add the time in one timeval structure with 
  1734. the time in another timeval structure.
  1735.  
  1736. Synopsis:
  1737.     
  1738. AddTime( tiem1, time2 );
  1739.  
  1740. time1:
  1741.     
  1742. (struct timeval *) Pointer to the first timeval structure. The value of the 
  1743. second timeval structure will be added with this timeval structure, and the 
  1744. result stored here.
  1745.  
  1746. time2:
  1747.     
  1748. (struct timeval *) Pointer to the second timeval structure.
  1749.  
  1750. SubTime()
  1751.  
  1752. The SubTime() function is used to subtract the time in one timeval structure 
  1753. with the time in another timeval structure.
  1754.  
  1755. Synopsos:
  1756.     
  1757. SubTime( tiem1, time2 );
  1758.  
  1759. time1:
  1760.     
  1761. (struct timeval *) Pointer to the first timeval structure. The value of the 
  1762. second timeval structure will be subtracted with this timeval structure, and 
  1763. the result stored here.
  1764.  
  1765. time2:
  1766.     
  1767. (struct timeval *) Pointer to the second timeval structure.
  1768.  
  1769. 2.6  EXAMPLES
  1770.  
  1771. Example 1
  1772. This example demonstrates how you can use the Timer Device. The program will be 
  1773. put to sleep for 10 seconds.
  1774.  
  1775. Example 2
  1776. This example demonstrates how you can send several requests to the Timer 
  1777. Device.
  1778.  
  1779. Example 3
  1780. This example demonstrates how you can use the Timer Device to get the current 
  1781. system time. We will then add two hours and set the new system time.
  1782.  
  1783. Example 4
  1784. This example demonstrates how you can compare, add and subtract time values 
  1785. with help of the timer device's own functions.
  1786.  
  1787.  
  1788. GAMEPORT DEVICE
  1789.  
  1790. 3.1  INTRODUCTION
  1791.  
  1792. All Amiga models have two contacts to which you can connect extra input devices 
  1793. like a mouse, joystick, trackball, proportional joystick etc. If Intuition is 
  1794. used, the left gameport is reserved for a mouse that controls the pointer. 
  1795. However, the right port can be used freely.
  1796.  
  1797. To monitor these two gameports you can uses the Gameport Device. It is a clean 
  1798. and polite way of controlling the gameports. You can of course go directly on 
  1799. the hardware and check what is happening. This is both easier and faster, but 
  1800. should only be used in games since it is not so polite to the system.
  1801.  
  1802. If you go directly on the hardware your program will be machine dependent, and 
  1803. may not run on future models of the Amiga.
  1804.  
  1805. I doubt that Commodore ever will try to change the hardware position of the 
  1806. gameport, but you never know. Programs that use the Gameport device will on the 
  1807. other hand always work, even if an Amiga 4000 is released. So if you can use 
  1808. the Gameport Device instead of hitting the hardware directly, stick to the 
  1809. device. 
  1810.  
  1811. 3.2  COMMON INPUT DEVICES FOR THE GAMEPORT
  1812.  
  1813. To the gameports you can connect several types of input devices, like: 
  1814. - Mouse
  1815. - Joystick
  1816. - Proportional (analogue) joystick
  1817. - Light pen
  1818. - Drawing (Digitizing) tablet
  1819. - Trackball
  1820.  
  1821. The most commonly used input devices is undoubtedly the mouse and joystick. 
  1822. However, inputdevices like analogue joysticks, light pens and digitizing 
  1823. tablets are getting more and more popular.
  1824.  
  1825. It is very common that a joystick is already connected to the right port. Most 
  1826. games expect that, but it does not mean that you can not connect a second mouse 
  1827. or proportional joystick there. It is a good rule to always tell the user what 
  1828. type of input device he/she should use, before your program starts.
  1829.  
  1830. 3.2.1  MOUSE
  1831.  
  1832. The mouse is the most commonly used device since all Amigas are old together 
  1833. with one. It is perfect for positioning cursors (like Intuition's pointer), or 
  1834. to move a gun-sight over some enemies.
  1835.  
  1836. A mouse usually consists of a rubber ball covered by a box with one or more 
  1837. buttons on top. When the user is sliding the mouse, the rubber ball is rotated 
  1838. and delta x and y movements are reported.
  1839.  
  1840. The normal Amiga mouse comes with two buttons, but there exist those with only 
  1841. one or three buttons.
  1842.  
  1843. 3.2.2  JOYSTICK
  1844.  
  1845. A normal joystick (also called absolute joystick) can only report 8 stick 
  1846. movements like: up, right, down, left and possible combinations like up and to 
  1847. the right. A joystick can have one or two buttons. Sadly most joystick comes 
  1848. with only one button.
  1849. 3.2.3  PROPORTIONAL JOYSTICK
  1850.  
  1851. A proportional (or analog) joystick is like a normal absolute joystick with one 
  1852. difference, it will report the exact delta position on the stick. The further 
  1853. in one direction, the higher number is broadcasted.
  1854.  
  1855. Proportional joysticks are perfect for flight simulators and similar programs. 
  1856. They are becoming more available nowadays, so it is a good idea to start 
  1857. supporting this type of device.
  1858.  
  1859. The Gameport Device can sadly not handle proportional joysticks for the moment 
  1860. which is a pity. However, I have included a small program in the "Input" drawer 
  1861. called "Analogue" which reads proportional (analogue) joystick movements 
  1862. directly from the hardware registers.
  1863.  
  1864. 3.2.4  LIGHT PEN
  1865.  
  1866. A light pen is a small device with which you can point anywhere on the screen 
  1867. in order to position a cursor or select an option. There have during the last 
  1868. years been a big discussion about which device, a mouse or a light pen, is 
  1869. easiest to use. I think it depends on what you are going to do. A pen is 
  1870. undoubtedly more natural for a beginner, but your arm gets very tired after 
  1871. some minutes, so it should not be used to much.
  1872.  
  1873. Luckily you do not need to bother about the light pen since it is also sadly 
  1874. not supported by the Gameport Device. However, newly made light pens usually 
  1875. comes with some software that replaces Intuition's mouse with the light pen. If 
  1876. you write your program as normal and is monitoring a mouse, the user can still 
  1877. connected and use a light pen. Your program whould not notice any difference.
  1878.  
  1879. 3.2.5  DRAWING (DIGITIZING) TABLET
  1880.  
  1881. Drawing (Digitizing) tablet is used in the same manner as a light pen, but with 
  1882. the advantage that your arm does not get so tired, and with the disadvantage 
  1883. that all your free space on your desk is suddenly occupied by a large sensitive 
  1884. tablet. (It is not recommended to spill hot coffee on it for example.)
  1885.  
  1886. Drawing tablets are not either supported by the Gameport Device. However, as 
  1887. same as with the light pen, most digitizing tablets comes with some software 
  1888. that replaces the normal mouse driver with its own driver. So if your program 
  1889. supports a mouse, it can also handle most digitizing tablets.
  1890.  
  1891. 3.2.5  TRACKBALL
  1892.  
  1893. A trackball is working exactly as a mouse, but with the advantage that you do 
  1894. not have to move it around. Since it acts exactly as a mouse you do not have to 
  1895. bother about any special routines for handling trackballs. Simply support mouse 
  1896. events and the user can connect a trackball instead.
  1897.  
  1898. 3.3  THE GAMEPORT DEVICE
  1899.  
  1900. The Gameport Device helps you to monitor the Amiga's two gameports. Port 1 
  1901. (unit 0) is usually already occupied by Intuition, but port 2 (unit 1) can 
  1902. normally be used. The Gameport Device supports for the moment only two types of 
  1903. input devices, and they are:
  1904.  
  1905. 1. Mouse (Trackball)
  1906. 2. Joystick
  1907.  
  1908. To prepare the Gameport Device to handle input events you have to:
  1909.  
  1910. 1. Create a message port with which the Gameport Device can communicate with 
  1911. you. [CreatePort()]
  1912. 2. Allocate an input/output request block (structure). [CreateStdIO()]     
  1913.  
  1914. 3. Open the Gameport Device [OpenDevice()]
  1915.  
  1916. 4. Check if some other task is already using the gameport. (A gameport can only 
  1917. be monitored by one task.)
  1918.  
  1919. 5. Tell the Gameport Device what type of input device you want to monitor. (For 
  1920. the moment you can only monitor mouse or joystick events.)
  1921.  
  1922. 6. Tell the Gameport Device what events should be reported, and when.
  1923.  
  1924. 7. Tell the Gameport Device that we want to start collecting gameport events.
  1925.  
  1926. 3.3.1  CREATE A MESSAGE PORT
  1927.  
  1928. As normal when you are using a device, you first have to create  message port 
  1929. with which the device can communicate with you. The simplest way is to create a 
  1930. message port by calling the CreatePort() function, which will return a pointer 
  1931. to a MsgPort structure (defined in the headerfile "exec/ports.h").
  1932.  
  1933. CreatePort() allocates and initializes a MsgPort structure. If you give it a 
  1934. string as first parameter it will also make the port public. If CreatePort() of 
  1935. some reason could not create a message port it returns NULL, otherwise if 
  1936. everything is OK it returns a pointer to the new MsgPort structure.
  1937.  
  1938. Synopsis
  1939. :
  1940.     
  1941. msgp = CreatePort( name, pri );
  1942.   
  1943. msgp:
  1944.     
  1945. (struct MsgPort *) Pointer to the new MsgPort structure, or NULL if something 
  1946. went wrong.
  1947.  
  1948. name:
  1949.     
  1950. (char *) Pointer to a string containing the name of the message port, or NULL. 
  1951. If it is a string the port will be made public (so other tasks can find it) 
  1952. else only our task can use it.
  1953.   
  1954. msgp:
  1955.     
  1956. (struct MsgPort *) Pointer to the MsgPort structure that should be allocated.
  1957.  
  1958. pri:
  1959.     
  1960. (BYTE) This message port's priority. Usually set to 0 - normal priority.
  1961.  
  1962. When your program terminates you must close the message port by calling the 
  1963. DeletePort() function!
  1964.  
  1965. DeletePort() deletes a message port. Every message port that has been allocated 
  1966. by CreatePort() must be deleted before the program terminates.
  1967.  
  1968. Synopsis
  1969. :
  1970.     
  1971. DeletePort( msgp );
  1972.  
  1973. msgp:
  1974.     
  1975. (struct MsgPort *) Pointer to the MsgPort structure, that should be 
  1976. deallocated.
  1977.  
  1978. Here is an example:
  1979.  
  1980.   /* Declare a pointer to our message port: */
  1981.   struct MsgPort  *game_msg_port;
  1982.  
  1983.   /* Create the message port: */
  1984.   /* (No name, priority 0.)   */
  1985.   game_msg_port = CreatePort( 0, 0 );
  1986.  
  1987.   /* Check if we have received a message port or not: */
  1988.   if( !game_msg_port )
  1989.     /* ERROR! Could not create message port! */
  1990.  
  1991.   ...
  1992.  
  1993.   /* Close message port: */
  1994.   DeletePort( game_msg_port );
  1995.  
  1996. 3.3.2  ALLOCATE AN INPUT/OUTPUT REQUEST BLOCK (STRUCTURE)
  1997.  
  1998. Secondly you need a I/O request structure (IOStdReq) in which you can store 
  1999. information like where the data is that shouldbe processed and how much data 
  2000. you send. When the Gameport Device has replied you can find error message, if 
  2001. any, in the structure etc. More about this later.
  2002.  
  2003. The IOStdReq structure should also be linked together with our message port. We 
  2004. will then receive a message each time the Gameport Device returns our I/O 
  2005. request.
  2006.  
  2007. The best way to allocate an IOStdReq structure is to call the CreateStdIO() 
  2008. function which will both allocate and pre-initialize the structure. (The 
  2009. IOStdReq structure is defined in the headerfile "exec/io.h".) We give 
  2010. CreateStdIO() a pointer to our message port, so it will automatically be linked 
  2011. together.
  2012.  
  2013. CreateStdIO() allocates and initializes a new IOStdReq structure. As only 
  2014. parameter you give it a pointer to a message port that will be used by the 
  2015. system to communicate with you. If the function succeeds, it returns a pointer 
  2016. to the new IoStdReq structure, else NULL which means something went wrong.
  2017.  
  2018. Synopsis
  2019. :
  2020.     
  2021. io = CreateStdIO( msgp );
  2022.   
  2023. io:
  2024.     
  2025. (struct IOStdReq *) Pointer to the new IOStdReq structure, or NULL if something 
  2026. went wrong.
  2027.  
  2028. msgp:
  2029.     
  2030. (struct MsgPort *) Pointer to a MsgPort structure with which the system can 
  2031. communicate with us.
  2032.  
  2033. Before your program terminates you must deallocate the request block by calling 
  2034. the DeleteStdIO() function.
  2035.  
  2036. DeleteStdIO() deallocates a IOStdReq structure that has been created by 
  2037. CreateStdIO(). Note that all allocated structures must be deleted before the 
  2038. program may terminate.
  2039.  
  2040. Synopsis
  2041. :
  2042.     
  2043. DeleteStdIO( io );
  2044.  
  2045. io:
  2046.     
  2047. (struct IOStdReq *) Pointer to the a IOStdReq structure that should be 
  2048. deallocated.
  2049.  
  2050. Here is an example:
  2051.  
  2052.   /* Declare a pointer to our I/O request block: */
  2053.   struct IOStdReq *game_io_msg;
  2054.  
  2055.   /* Allocate and initialize a new I/O request block:    */
  2056.   /* (It should use our new message port as reply port.) */
  2057.   game_io_msg = CreateStdIO( game_msg_port );
  2058.  
  2059.   /* Check if we have allocated the req. block successfully: */
  2060.   if( !game_io_msg )
  2061.     /* ERROR! Could not allocate new I/O request block! */
  2062.  
  2063.   ...
  2064.  
  2065.   /* Deallocate I/O request block: */
  2066.   DeleteStdIO( game_io_msg );
  2067.  
  2068. 3.3.3  OPEN THE GAMEPORT DEVICE
  2069.  
  2070. Now you can open the Gameport Device with help of the OpenDevice() function. 
  2071. When you open this device you must also tell the system which port you want to 
  2072. monitor (unit 0 - the "mouse port" or unit 1 - the "joystick" port). The 
  2073. function will return 0 if everything is OK, or an error number if it could not 
  2074. open the Gameport Device.
  2075.  
  2076. OpenDevice() will try to open the specified device. It will return 0 if 
  2077. everything went OK, else an error number is returned.
  2078.   
  2079. Synopsis
  2080. :
  2081.     
  2082. error = OpenDevice( name, unit, io, flags );
  2083.  
  2084. error:
  2085.     
  2086. (long) OpenDevice() will return 0 if everything went OK, or an error number if 
  2087. something went wrong.
  2088.  
  2089. namne:
  2090.     
  2091. (char *) Pointer to a string containing the name of the device you want to 
  2092. open. The name of the Gameport Device is "gameport.device".
  2093.  
  2094. unit:
  2095.     
  2096. (long) Which unit should be used. The Gameport Device can handle two ports: 
  2097.     
  2098. unit 0 - the left "mouse port", port 1.
  2099.     
  2100. unit 1 - the right "joystick port", port 2. 
  2101.  
  2102. io:
  2103.     
  2104. (struct IORequest *) Pointer to an already initialized IORequest or IOStdReq 
  2105. structure.
  2106.  
  2107. flags:
  2108.     
  2109. (long) Additional information, set to 0.
  2110.  
  2111. Before your program terminates you have to close the Gameport Device by calling 
  2112. the CloseDevice() function. CloseDevice() tries to close a device that has 
  2113. previously been opened by an OpenDevice() call.
  2114.  
  2115. Synopsis
  2116. :
  2117.     
  2118. CloseDevice( io );
  2119.  
  2120. io:
  2121.     
  2122. (struct IORequest *) Pointer to an IORequest or IOStdReq structure that has 
  2123. been used as parameter in the OpenDevice() function call.
  2124.  
  2125. Here is an example:
  2126.  
  2127.   /* Declare a boolean variable which will be TRUE if    */
  2128.   /* something when wrong, or FALSE if everything is OK: */
  2129.   BOOL deviceerror;
  2130.  
  2131.   /* Open the Game Port Device, use the right port (unit 1): */
  2132.   deviceerror =
  2133.      OpenDevice( "gameport.device", 1, game_io_msg, 0 );
  2134.  
  2135.   /* Check if we have opened the device or not: */
  2136.   if( deviceerror )
  2137.     clean_up( "ERROR! Could not open the Game Port Device!" );
  2138.  
  2139.   ...
  2140.  
  2141.   /* Close the Gameport Device: */
  2142.   CloseDevice( game_io_msg );
  2143.  
  2144. 3.3.4  CHECK IF SOME OTHER TASK IS ALREADY USING THE PORT
  2145.  
  2146. It is now important to check if some other task is already monitoring the port, 
  2147. and if so we can not try to use it ourself.
  2148.  
  2149. Set the io_Command field of the IOStdReq structure to GPD_ASKCTYPE. This tells 
  2150. the system that you want to know what type of controller, if any, is for the 
  2151. moment using the port. We must now also give the structure (io_Data) a pointer 
  2152. to at least one byte of free memory where the answer will be stored, and tell 
  2153. the structure how much memory we gave it (io_Length). We can now give our order 
  2154. to the Gameport Device by calling the DoIO() function.
  2155.  
  2156. The DoIO() function will return as soon as the Gameport Device has done our 
  2157. request. We should now be able to find the answer at that memory position we 
  2158. specified, and if it is not equal to GPCT_NOCONTROLLER some other task is 
  2159. already using the port.
  2160.  
  2161. Here is an example:
  2162.  
  2163.   /* Put the answer in this variable: */
  2164.   BYTE type;
  2165.  
  2166.   /* Create message port, allocate IOStdReq structure and */
  2167.   /* open the Gameport Device.                            */
  2168.  
  2169.   /* We want to know what controller, if any, is used: */
  2170.   game_io_msg->io_Command = GPD_ASKCTYPE;
  2171.  
  2172.   /* The reply should only be one byte long: */
  2173.   game_io_msg->io_Length = 1;
  2174.  
  2175.   /* Where we want the data stored: */
  2176.   game_io_msg->io_Data = (APTR) &type;  
  2177.  
  2178.   /* Do our request, and return when it is done: */
  2179.   DoIO( game_io_msg );  
  2180.  
  2181.   /* Check if some other task is already using the gameport: */
  2182.   if( type)
  2183.   {
  2184.     /* YES! Some other task is already using this port! */
  2185.     /* Lets check what type of controller he is using:  */
  2186.     switch( type )
  2187.     {
  2188.       case GPCT_MOUSE:
  2189.         printf( "A mouse is connected to the port!\n" );
  2190.         break;
  2191.  
  2192.       case GPCT_RELJOYSTICK:
  2193.         printf( "A proportional joystick is connected to the port!\n" );
  2194.         break;
  2195.  
  2196.       case GPCT_ABSJOYSTICK:
  2197.         printf( "A normal joystick is connected to the port!\n" );
  2198.         break;
  2199.     }
  2200.     /* We may now NOT close the device! If we do it, the other  */
  2201.     /* task will then not be able to use the gameport device    */
  2202.     /* either! We should only deallocate the StdIOReq structure */
  2203.     /* and close the message port!                              */
  2204.  
  2205.     /* Deallocate I/O request block: */
  2206.     DeleteStdIO( game_io_msg );
  2207.  
  2208.     /* Close message port: */
  2209.     DeletePort( game_msg_port );
  2210.  
  2211.     exit( ERROR );
  2212.   }
  2213.   else
  2214.   {
  2215.     /* OK! No other task is using the port! */
  2216.   }
  2217.  
  2218. 3.3.5  SET TYPE OF CONTROLLER
  2219.  
  2220. We should now tell the Gameport Device what type of controller (mouse or 
  2221. joystick) we want to monitor. To do this set the io_Command field of the 
  2222. IOStdReq structure to GPD_SETCTYPE. We must also give the field io_Data a 
  2223. pointer to at least one byte of free memory where our request (mouse - 
  2224. GPD_MOUSE or joystick- GPD_ABSJOYSTICK) is stored, and set the io_Length to 1 
  2225. (we send 1 byte).
  2226.  
  2227. We can now give our order to the Gameport Device by calling the DoIO() 
  2228. function.
  2229.  
  2230.   /* We want to monitor mouse events: */
  2231.   BYTE type = GPCT_MOUSE;
  2232.  
  2233.   /* We want to set controller type: */
  2234.   game_io_msg->io_Command = GPD_SETCTYPE;
  2235.  
  2236.   /* The message is only one byte long: */
  2237.   game_io_msg->io_Length = 1;
  2238.  
  2239.   /* The data we want to send: */
  2240.   game_io_msg->io_Data = (APTR) &type;  
  2241.  
  2242.   /* Do our request, and return when it is done: */
  2243.   DoIO( game_io_msg );
  2244.  
  2245. When your program terminates it must set controller type back to 
  2246. GPCT_NOCONTROLLER, before you close the Gameport Device! Do as above except 
  2247. that you set type to GPCT_NOCONTROLLER.
  2248.  
  2249.   /* We want to clear the port: */
  2250.   BYTE type = GPCT_NOCONTROLLER;
  2251.  
  2252.   /* We want to set controller type: */
  2253.   game_io_msg->io_Command = GPD_SETCTYPE;
  2254.  
  2255.   /* The message is only one byte long: */
  2256.   game_io_msg->io_Length = 1;
  2257.  
  2258.   /* The data we want to send: */
  2259.   game_io_msg->io_Data = (APTR) &type;  
  2260.  
  2261.   /* Do our request, and return when it is done: */
  2262.   DoIO( game_io_msg );
  2263.  
  2264.   /* You may now close the Gameport Device, deallocate the */
  2265.   /* IOStdReq structure and delete the message port.       */
  2266.  
  2267. 3.3.6  SET TRIGGER
  2268.  
  2269. No almost everything is prepared. The last thing you have to do is to tell the 
  2270. Gameport Device when you want to receive gameport events. There exist five 
  2271. different types of event that may occur if you want, and they are:
  2272.  
  2273. 1. A button was pressed. (GPTF_DOWNKEYS)
  2274. 2. A button was released. (GPTF_UPKEYS)
  2275. 3. Timeout. Nothing has happened within a specified timelimit.
  2276. 4. Delta X movements. (Mouse or stick moved to the right/left.)
  2277. 5. Delta Y movements. (Mouse or stick moved up/down.)
  2278.  
  2279. You set what type of trigger you want by initializing the GamePortTrigger 
  2280. structure (defined in the headerfile "devices/gameport.h"). The GamePortTrigger 
  2281. structure look like this:
  2282.  
  2283. struct GamePortTrigger
  2284. {
  2285.   UWORD gpt_Keys;
  2286.   UWORD gpt_Timeout;
  2287.   UWORD gpt_XDelta;
  2288.   UWORD gpt_YDelta;
  2289. };
  2290.  
  2291. gpt_Keys:
  2292.     
  2293. If you want to trigger an event when a button is pressed, set flag 
  2294. GPTF_DOWNKEYS. If you want to trigger an event when a button is released, set 
  2295. flag GPTF_UPKEYS. If you want to trigger an event both when a button is pressed 
  2296. as well as when it is released, set both flags with the binary operator | in 
  2297. between, GPTF_DOWNKEYS|GPTF_UPKEYS.
  2298.  
  2299. gpt_Timeout:
  2300.     
  2301. If this value is not zero, a timeout message will be triggered if nothing has 
  2302. happened during the specified timeperiod. The value is in vertical blank units 
  2303. which occurs 60 times a second. So if you want a message to be triggered after 
  2304. 30 seconds, set gpt_Timeout to 30 * 60 = 1800.
  2305.  
  2306. gpt_XDelta:
  2307.     
  2308. How many x counts should occur before a message is broadcasted. If you are 
  2309. monitoring a normal joystick, set this value to 1. 
  2310.  
  2311. gpt_YDelta:
  2312.     
  2313. How many y counts should occur before a message is broadcasted. If you are 
  2314. monitoring a normal joystick, set this value to 1. 
  2315.  
  2316. Fill this GamePortTrigger structure with your requirements, and give the 
  2317. IOStdReq structure a pointer to it. Set the io_Command field to GPD_SETTRIGGER 
  2318. (we want to tell the system what should trigger an event). Finally do not 
  2319. forget that the field io_Length should now be set to: sizeof( struct 
  2320. GamePortTrigger).
  2321.  
  2322. Here is an example:
  2323.  
  2324.   /* This structure will be filled with our requirements: */
  2325.   struct GamePortTrigger gpt;
  2326.  
  2327.   /* Set our requirements: */
  2328.   gpt.gpt_Keys = GPTF_DOWNKEYS|GPTF_UPKEYS; /* Up or down. */
  2329.   gpt.gpt_Timeout = 600;                    /* 10 seconds. */
  2330.   gpt.gpt_XDelta = 5;                       /* At least 5  */
  2331.   gpt.gpt_YDelta = 5;                       /* counts.     */
  2332.  
  2333.   /* We want to set controller trigger: */
  2334.   game_io_msg->io_Command = GPD_SETTRIGGER;
  2335.  
  2336.   /* The message is sizeof(struct GamePortTrigger) bytes long: */ 
  2337.   game_io_msg->io_Length = sizeof( gpt );
  2338.  
  2339.   /* Pointer to the data we want to send: */
  2340.   game_io_msg->io_Data = (APTR) &gpt;
  2341.  
  2342.   /* Do our request and return when it is done: */
  2343.   DoIO( game_io_msg );
  2344.  
  2345. 3.3.7  PREPARE TO READ
  2346.  
  2347. The last thing we have to do is to prepare the Gameport Device to send us 
  2348. messages each time a gameport event is triggered. All information about the 
  2349. gameport event is automatically stored in an InputEvent structure (defined in 
  2350. the headerfile "devices/inputevent.h"), so what we have to do is to give the 
  2351. Gameport Device a pointer to an InputEvent structure, specify the size to 
  2352. sizeof( struct InputEvent), and set the io_command to GPD_READEVENT.
  2353.  
  2354. Here is an example:
  2355.  
  2356.   /* Store the information about the gameport event */
  2357.   /* in this structure:                             */
  2358.   struct InputEvent data;
  2359.  
  2360.   /* We want to read gameport events: */
  2361.   game_io_msg->io_Command = GPD_READEVENT;  
  2362.  
  2363.   /* The gameport event is sizeof(struct InputEvent) bytes long: */
  2364.   game_io_msg->io_Length = sizeof( data );  
  2365.  
  2366.   /* Where we want the data to be placed: */
  2367.   game_io_msg->io_Data = (APTR) &data;
  2368.  
  2369.   /* Do not use quick io: */
  2370.   game_io_msg->io_Flags = 0;
  2371.  
  2372. 3.4  HOW TO MONITOR THE GAMEPORT
  2373.  
  2374. Once the Gameport Device has been properly prepared, you can start monitoring 
  2375. the port. Each time you want to check the port you send your request with help 
  2376. of the function SendIO().
  2377.  
  2378.   /* Do our request, and return without delay: */
  2379.   SendIO( game_io_msg );
  2380.  
  2381. You will now receive a message at our message port as soon as a gameport event 
  2382. is triggered (the user has moved the joystick, or a button has been pressed for 
  2383. example). If we do not want to do anything while we are waiting for a gameport 
  2384. event, it is best to call the WaitPort() function. It will put our task to 
  2385. sleep so we do not waste any computer time. However, once there appear a 
  2386. message at our port, our task is woken up.
  2387.  
  2388.   /* Wait for a message to arrive at our message port. */
  2389.   /* While we are waiting our task is put to sleep,    */
  2390.   /* which means that we will not waste any computer   */ 
  2391.   /* time. Zzz Zzz Zzz...                              */
  2392.   WaitPort( game_msg_port );
  2393.  
  2394. Once we believe there is a message at our port, we can collect it with help of 
  2395. the GetMsg() function. If we could not collect a message, GetMsg() will return 
  2396. NULL. When we have collected a message we know that the Gameport Device has 
  2397. filled our InputEvent structure with some interesting information about the 
  2398. gameport event.
  2399.  
  2400. It is important to remember that from that moment we send our request with 
  2401. SendIO() until we collect a message with GetMsg() we are not allowed to use the 
  2402. InputEvent structure. However, once we have collected a message we may examine 
  2403. it or change it as much as we want, until we send another request with 
  2404. SendIO().
  2405.  
  2406.   /* Try to Collect a message: */
  2407.   if( GetMsg( game_msg_port ) )
  2408.   {
  2409.     /* If we have successfully collected a message, */
  2410.     /* we may examine the InputEvent structure.     */
  2411.   }
  2412.  
  2413. PLEASE NOTE:
  2414.   
  2415. 1. Before you can wait for a gameport event you must have sent a gameport 
  2416. request by calling the SendIO() function.
  2417.  
  2418. 2. Before your program terminates you must have collected every gameport 
  2419. request you have sent! If you close the message port before you have answered 
  2420. all events, something, not so nice, will happen.
  2421.  
  2422. 3.4.1  THE INPUTEVENT STRUCTURE
  2423.  
  2424. As we said above, once you receive a gameport event you should examine the 
  2425. InputEvent structure. The structure look like this: (Defined in the headerfile 
  2426. "devices/inputevent.h".)
  2427.  
  2428. struct InputEvent
  2429. {
  2430.   struct InputEvent *ie_NextEvent;
  2431.   UBYTE ie_Class;
  2432.   UBYTE ie_SubClass;
  2433.   UWORD ie_Code;
  2434.   UWORD ie_Qualifier;
  2435.   union
  2436.   {
  2437.     struct
  2438.     {
  2439.       WORD ie_x;
  2440.       WORD ie_y;
  2441.     } ie_xy;
  2442.     APTR ie_addr;
  2443.   } ie_position;
  2444.   struct timeval ie_TimeStamp;
  2445. };
  2446.  
  2447. /* These constants (together with many more) are also */
  2448. /* declared in the headerfile:                        */
  2449. #define ie_X             ie_position.ie_xy.ie_x
  2450. #define ie_Y             ie_position.ie_xy.ie_y
  2451. #define ie_EventAddress ie_position.ie_addr
  2452.  
  2453. ie_NextEvent:
  2454.     
  2455. Pointer to the next InputEvent structure. (All events are stored 
  2456. chronologically.)
  2457.  
  2458.  
  2459. ie_Class:
  2460.     
  2461. What type of message it is. Not currently used by the Gameport Device.
  2462.  
  2463.  
  2464. ie_SubClass:
  2465.     
  2466. Extra field, sometimes used to more precisely explain what has happened. Not 
  2467. currently used by the Gameport Device.
  2468.  
  2469.  
  2470. ie_Code:
  2471.     
  2472. This field tells us if a button has been pressed, or released. (Remember that 
  2473. you will only receive such messages if you have set the gameport event triggers 
  2474. GPTF_DOWNKEYS and/or GPTF_UPKEYS.)
  2475.               
  2476.     
  2477. If you are monitoring joystick events, and the user presses the fire button, 
  2478. this field is set to IECODE_LBUTTON. If the user later on releases the fire 
  2479. button, this field is set to IECODE_LBUTTON + IECODE_UP_PREFIX.
  2480.               
  2481.     
  2482. If you are monitoring mouse events, this field can contain one of the following 
  2483. six different types of messages:
  2484.  
  2485.     
  2486. IECODE_LBUTTON: Left mouse button pressed.
  2487.  
  2488.     
  2489. IECODE_MBUTTON: Middle mouse button pressed.(*)
  2490.  
  2491.     
  2492. IECODE_RBUTTON: Right mouse button pressed.
  2493.  
  2494.     
  2495. IECODE_LBUTTON+IECODE_UP_PREFIX: Left mouse button was released.
  2496.  
  2497.     
  2498. IECODE_MBUTTON+IECODE_UP_PREFIX: Middle mouse button was released. (*)
  2499.  
  2500.     
  2501. IECODE_RBUTTON+IECODE_UP_PREFIX: Right mouse button was released.
  2502.  
  2503.     
  2504. [(*) Does not exist on the standard Amiga mouse.]
  2505.  
  2506.     
  2507. If no button was pressed or released, this field is set to IECODE_NOBUTTON.
  2508.  
  2509. ie_Qualifier:
  2510.     
  2511. Extra information for the code field. Not used by the Gameport Device.
  2512.  
  2513. ie_x:
  2514.     
  2515. Delta x movements.
  2516.  
  2517.     
  2518. If you are monitoring joystick events, 1 means that the stick was move to the 
  2519. right, and -1 means that the stick was moved to the left.
  2520.  
  2521.     
  2522. If you are monitoring mouse events, this fields tells you how many x counts the 
  2523. mouse has moved.
  2524.  
  2525. ie_y:
  2526.     
  2527. Delta y movements.
  2528.  
  2529.     
  2530. If you are monitoring joystick events, 1 means that the stick was move back, 
  2531. and -1 means that the stick was moved forward.
  2532.  
  2533.     
  2534. If you are monitoring mouse events, this fields tells you how many y counts the 
  2535. mouse has moved.
  2536.  
  2537. ie_addr:
  2538.     
  2539. The address of the device that sent the message.
  2540.  
  2541. ie_TimeStamp:
  2542.     
  2543. System timer.
  2544.  
  2545. 3.4.1  COLLECT JOYSTICK EVENTS
  2546.  
  2547. Here is an example on how to collect joystick events: ("data" is a pointer to 
  2548. an initialized InputEvent structure.)
  2549.  
  2550.   /* Store the direction of the stick and */
  2551.   /* button position in these variables:  */
  2552.   WORD xdirection;
  2553.   WORD ydirection;
  2554.   UWORD code;
  2555.  
  2556.   /* Collect data: */
  2557.   xdirection = data->ie_X;
  2558.   ydirection = data->ie_Y;
  2559.   code = data->ie_Code;
  2560.  
  2561.  
  2562.   /* Was the fire button pressed or released? */
  2563.   if( code == IECODE_LBUTTON ) 
  2564.     printf("Button pressed. ");
  2565.  
  2566.   if( code == IECODE_LBUTTON + IECODE_UP_PREFIX )
  2567.     printf("Button released. ");
  2568.  
  2569.  
  2570.   /* What is the position of the stick: */
  2571.   switch(ydirection) 
  2572.   {
  2573.     case -1:
  2574.       printf( "Forward" );
  2575.       break;
  2576.  
  2577.     case 1:
  2578.       printf( "Back" );
  2579.       break;
  2580.   }
  2581.  
  2582.   switch(xdirection) 
  2583.   {
  2584.     case -1:
  2585.       printf( "Left" );
  2586.       break;
  2587.  
  2588.     case 1:
  2589.       printf( "Right" );
  2590.       break;
  2591.   }
  2592.  
  2593. 3.4.2  COLLECT MOUSE EVENTS
  2594.  
  2595. Here is an example on how to collect mouse events: ("data" is a pointer to an 
  2596. initialized InputEvent structure.)
  2597.  
  2598.   WORD x;
  2599.   WORD y;
  2600.   UWORD code;
  2601.  
  2602.   /* Collect data: */
  2603.   x = data->ie_X;
  2604.   y = data->ie_Y;
  2605.   code = data->ie_Code;
  2606.  
  2607.  
  2608.   /* Check if an mouse button has been pressed/released: */
  2609.   switch( code )
  2610.   {
  2611.     case IECODE_LBUTTON:
  2612.       printf( "Left mouse button pressed" );
  2613.       break;
  2614.  
  2615.     case IECODE_MBUTTON:
  2616.       printf( "Middle mouse button pressed" );
  2617.       break;
  2618.  
  2619.     case IECODE_RBUTTON:
  2620.       printf( "Right mouse button pressed" );
  2621.       break;
  2622.  
  2623.     case IECODE_LBUTTON + IECODE_UP_PREFIX:
  2624.       printf( "Left mouse button released" );
  2625.       break;
  2626.  
  2627.     case IECODE_MBUTTON + IECODE_UP_PREFIX:
  2628.       printf( "Middle mouse button released" );
  2629.       break;
  2630.  
  2631.     case IECODE_RBUTTON + IECODE_UP_PREFIX:
  2632.       printf( "Right mouse button released" );
  2633.       break;
  2634.  
  2635.     case IECODE_NOBUTTON:
  2636.       printf( "No buttons pressed/released" );
  2637.       /* This meant that you have either received a timeout */
  2638.       /* message, or the mouse has been moved.              */
  2639.       break;
  2640.   }
  2641.   
  2642.   
  2643.   /* Check delta movement: */
  2644.   printf( "Delta X: %4d   Delta Y: %4d\n", x, y );
  2645.  
  2646. 3.5  FUNCTIONS
  2647.  
  2648. Note that several of these functions are rather complicated, and have not been 
  2649. fully explained in this chapter. For moreinformation se chapter XXXXX. Here is 
  2650. at least a short description of what they are doing, and how they should be 
  2651. used.
  2652.  
  2653. CreatePort()
  2654.  
  2655. This function allocates and initializes a MsgPort structure. If you give it a 
  2656. string as first parameter it will also make the port public. If CreatePort() of 
  2657. some reason could not create a message port it returns NULL, otherwise if 
  2658. everything is OK it returns a pointer to the new MsgPort structure.
  2659.  
  2660. Note that a port that has been created by calling CreatePort(), must be 
  2661. deallocated before your program terminates. The easiest way to deallocate a 
  2662. message port is to use the DeletePort() function.
  2663.  
  2664. Synopsis:
  2665.     
  2666. msgp = CreatePort( name, pri );
  2667.   
  2668. msgp:
  2669.     
  2670. (struct MsgPort *) Pointer to the new MsgPort structure, or NULL if something 
  2671. went wrong.
  2672.  
  2673. name:
  2674.     
  2675. (char *) Pointer to a string containing the name of the message port, or NULL. 
  2676. If it is a string the port will be made public (so other tasks can find it) 
  2677. else only our task can use it.
  2678.   
  2679. msgp:
  2680.     
  2681. (struct MsgPort *) Pointer to the MsgPort structure that should be deallocated.
  2682.  
  2683. pri:
  2684.     
  2685. (BYTE) This message port's priority. Usually set to 0 - normal priority.
  2686.  
  2687. DeletePort()
  2688.  
  2689. This function deletes a message port. Every message port that has been 
  2690. allocated by CreatePort() must be deleted before the program terminates.
  2691.  
  2692. Synopsis:
  2693.     
  2694. DeletePort( msgp );
  2695.  
  2696. msgp:
  2697.     
  2698. (struct MsgPort *) Pointer to the MsgPort structure, that should be 
  2699. deallocated.
  2700.  
  2701. CreateStdIO()
  2702.  
  2703. This function allocates and initializes a new IOStdReq structure. As only 
  2704. parameter you give it a pointer to a message port that will be used by the 
  2705. system to communicate with you. If the function succeeds, it returns a pointer 
  2706. to the new IoStdReq structure, else NULL which means something went wrong.
  2707.  
  2708. Note that a IOStdReq structure that has been allocated by CreateStdIO(), must 
  2709. be deallocated before the program terminates. To do this, use the function 
  2710. DeleteStdIO().
  2711.  
  2712. Synopsis:
  2713.     
  2714. io = CreateStdIO( msgp );
  2715.   
  2716. io:
  2717.     
  2718. (struct IOStdReq *) Pointer to the new IOStdReq structure, or NULL if something 
  2719. went wrong.
  2720.  
  2721. msgp:
  2722.     
  2723. (struct MsgPort *) Pointer to a MsgPort structure with which the system can 
  2724. communicate with us.
  2725.  
  2726. DeleteStdIO()
  2727.  
  2728. This function deallocates a IOStdReq structure that has been created by 
  2729. CreateStdIO(). Note that all allocated structures must be deleted before the 
  2730. program may terminate.
  2731.  
  2732. Synopsis:
  2733.     
  2734. DeleteStdIO( io );
  2735.  
  2736. io:
  2737.     
  2738. (struct IOStdReq *) Pointer to the a IOStdReq structure that should be 
  2739. deallocated.
  2740.  
  2741. OpenDevice()
  2742.  
  2743. This function will try to open the specified device. It will return 0 if 
  2744. everything went OK, else an error number is returned.
  2745.   
  2746. Note that if you have opened a device, your program must close it before it 
  2747. terminates. However, if you have opened a device that is already being used by 
  2748. some other task you should not close it. Close a device by calling the 
  2749. CloseDevice() function.
  2750. Synopsis:
  2751.     
  2752. error = OpenDevice( name, unit, io, flags );
  2753.  
  2754. error:
  2755.     
  2756. (long) OpenDevice() will return 0 if everything went OK, or an error number if 
  2757. something went wrong.
  2758.  
  2759. name:
  2760.     
  2761. (char *) Pointer to a string containing the name of the device you want to 
  2762. open. The name of the Gameport Device is "gameport.device".
  2763.  
  2764. unit:
  2765.     
  2766. (long) Which unit should be used. The Gameport Device can handle two ports:
  2767.     
  2768. unit 0 - the left "mouse port", port 1.
  2769.     
  2770. unit 1 - the right "joystick port", port 2. 
  2771.  
  2772. io:
  2773.     
  2774. (struct IORequest *) Pointer to an already initialized IORequest or IOStdReq 
  2775. structure.
  2776.  
  2777. flags:
  2778.     
  2779. (long) Additional information, set to 0.
  2780.  
  2781. CloseDevice()
  2782.  
  2783. This function closes a device that has previously been opened by an 
  2784. OpenDevice() call. Note that if you have opened a device, your program must 
  2785. close it before it terminates. However, if you have opened a device that is 
  2786. already being used by some other task you should not close it.
  2787.  
  2788. Synopsis:
  2789.     
  2790. CloseDevice( io );
  2791.  
  2792. io:
  2793.     
  2794. (struct IORequest *) Pointer to an IORequest or IOStdReq structure that has 
  2795. been used as parameter in the OpenDevice() function call.
  2796.  
  2797. 3.6  EXAMPLES
  2798.  
  2799. Example1
  2800. This example demonstrates how to open the Gameport Device, and monitor Joystick 
  2801. events. While we are waiting we put our task to sleep so we do not waste any 
  2802. computer time.
  2803.  
  2804. Example2
  2805. Same as example 1, but instead of putting the task to sleep while we are 
  2806. waiting for something to happen, we constantly try to receive joystick events. 
  2807. This should for example be used in games. I am sure that you so not want that 
  2808. all aliens should stop attacking the world just because the user has not moved 
  2809. the stick.
  2810.  
  2811. Example3
  2812. This example demonstrates how to open the Gameport Device, and monitor mouse 
  2813. events. While we are waiting we put our task to sleep so we do not waste any 
  2814. computer time.
  2815.  
  2816. Example4
  2817. Same as example 3, but instead of putting the task to sleep while we are 
  2818. waiting for something to happen, we constantly try to receive mouse events.
  2819.  
  2820.  
  2821. AUDIO DEVICE
  2822.  
  2823. 4.1  INTRODUCTION
  2824.  
  2825. The Amiga was designed to be the first home computer which combined state of 
  2826. the art graphics and impressive sound capabilities. Although more than six 
  2827. years have passed since the first Amiga was released, very few other computers 
  2828. can compete with the sound quality and flexibility offered by the Amiga.
  2829.  
  2830. In this chapter we look at how you can use the sound system to produce sound 
  2831. effects as well as compose melodies. Since the sound system has been built into 
  2832. the rest of the operating system, it will work smoothly together with other 
  2833. processes, and can without problems be used by several tasks at the same time.
  2834.  
  2835. 4.1.1  SOUND
  2836.  
  2837. Sound is simply vibrating air that is registered by our ears and interpreted as 
  2838. lovely music, speech, white noise, etc. Although the simplicity it is possible 
  2839. to produce almost unlimited number of variations of even a simple tune. Even a 
  2840. single note sound very different if it was made by a piano or violin. By 
  2841. altering the volume and rate it is played it is possible to produce even more 
  2842. variations. 
  2843.  
  2844. To better understand what sound is and how it is produced we will first look at 
  2845. some commonly used technical words.
  2846.  
  2847. The "amplitude" of sound is the height of one of the waveforms. The higher 
  2848. amplitude the louder will the waveform sound.
  2849.  
  2850. The "frequency" is the number of waveforms produced each second. The higher 
  2851. frequency the higher will the tone be.
  2852.  
  2853. There exist a third sound variable called "timbre". The problem with it is that 
  2854. it is very hard to describe, but is still very important for how the sound will 
  2855. be perceived. It can be called "the sound identity". A violin and a piano can 
  2856. play a tone with the same amplitude and frequency, but it will still sound very 
  2857. different. Even two violins can sound very different.
  2858.  
  2859. The problem with reproducing the timbre is that by altering the frequency or 
  2860. amplitude just a little (does not even need to be a noticeable change) it can 
  2861. completely change the timbre. If you have read about "chaos theories" the 
  2862. timbre can be described with the "butterfly effect"; even a small change in 
  2863. wind by a butterfly can tip the balance and in the end cause a hurricane on the 
  2864. other side of the world.
  2865.  
  2866. By combining several frequencies each with its own amplitude and timbre we can 
  2867. produce unlimited numbers of variations.
  2868.  
  2869. 4.1.2  DIFFERENT WAVEFORMS
  2870.  
  2871. Although each waveform from a note is different from another we usually do not 
  2872. bother too much about it. It is usually enough to use just a few different 
  2873. elementary waveforms. By altering the frequency and amplitude of these 
  2874. waveforms we can then still produce millions of different sounds.
  2875.  
  2876. The most commonly used waveforms are the sine, triangular and square waveforms. 
  2877. See illustration "Waveforms".
  2878.  
  2879.  
  2880. 4.1.3  DIGITAL AND ANALOG WAVEFORMS
  2881.  
  2882. When we are using computers we can only handle digital numbers. To convert 
  2883. these digital numbers into sound we must first convert the digital numbers to 
  2884. analog values which can then be sent to a loud speaker. See illustration 
  2885. "AnalogDigital".
  2886.  
  2887. On each Amiga computer there exist four "sound channels" which each can convert 
  2888. digital numbers into analogue values. We can therefore play four different 
  2889. sounds at the same time, which means that very complex sounds can be produced.
  2890.  
  2891. Two of the sound channels are connected to the right audio port, and the other 
  2892. two are connected to the left audio port. We can therefore produce stereo sound 
  2893. on the Amiga.
  2894.  
  2895. The sound channels are sometimes called "eight-bit channels" because they 
  2896. convert eight-bit values into analog values. The Amiga can therefore produce 
  2897. sounds were the values in the waveforms ranges from -128 to 127. Compact Disk 
  2898. (CD) players use 16 bits and can therefore use even more values. For computers 
  2899. today eight bits are usually more than enough, and can produce very impressive 
  2900. sounds.
  2901.  
  2902. 4.1.4  PLAY SAMPLED SOUNDS OR CREATE YOUR OWN TUNES
  2903.  
  2904. On the Amiga you can create your own tunes by using a waveform and modulating 
  2905. it (changing the amplitude and frequency). You can also play sound data that 
  2906. has been sampled (converted from analog sound to digital data). By using 
  2907. sampled data you can reproduce very complex waveforms, and it will sound 
  2908. natural.
  2909.  
  2910. To load and use already sampled data can very easily be done with help of the 
  2911. two utilities I have included in the "Sound" manual, "Easy Sound" and "Include 
  2912. Sound".
  2913.  
  2914. 4.2  PREPARE THE AUDIO DEVICE
  2915. 4.2.1  PRIORITY
  2916. Since several programs may be running at the same time it may happen that two 
  2917. or more programs tries to use the same sound channel at the same time. To solve 
  2918. problems like this we have
  2919. to use priorities.
  2920.  
  2921. If a program tries to use a sound channel that some other program is already 
  2922. using two things may happen. If the new program has the same or lower priority 
  2923. than the other program the new one is refused any access to the sound channel 
  2924. and the old program may continue to use it. On the other hand, if the new 
  2925. program has higher priority it will "steal" the sound channel from the old 
  2926. program.
  2927.  
  2928. Some sounds are more important than others. An emergency alarm is of course 
  2929. much more important than a background tune, and thus the emergency alarm should 
  2930. have higher priority. Commodore has suggested the following priorities:
  2931.   Type of sound             Suggested priority
  2932.   --------------------------------------------
  2933.   Unstoppable sound               127
  2934.   Emergencies                      95
  2935.   Attention                        85
  2936.   Speech                           75
  2937.   Information                      60
  2938.   Music                             0
  2939.   Sound effects                   -35
  2940.   Background music                -90
  2941.   Silence                        -128
  2942. If a sound is so important that no one what ever happens should be able to 
  2943. interrupt it we should use the "unstoppable sound priority". You should never 
  2944. start a sound with this priority. You should instead start it with a lower (for 
  2945. example with the "emergency") priority, and then increase it.
  2946.  
  2947. The "emergency" priority should be used when something happens that is so 
  2948. important that the user must immediately know about it. Examples: the program 
  2949. is going to visit the guru if the user does not immediately do some thing. 
  2950.  
  2951. The "attention" priority is used to alert the user that something has happen 
  2952. and the user must as soon as possible know about it.
  2953.  
  2954. The "speech" priority is used by the "Narrator Device" when it is producing 
  2955. synthesized speech. Spoken information is usually more important for the user 
  2956. than a nice tune.
  2957.  
  2958. The "information" priority should be used when you want to tell the user 
  2959. something, but the user must not immediately know about it. This priority can 
  2960. be used when you want to tell the user to read something, or start to input 
  2961. some value etc...
  2962.  
  2963. When you are plying music you should use the "music" priority. This is for 
  2964. programs that are using music as one of the primary functions. Background tunes 
  2965. should use the "background priority".
  2966.  
  2967. The lowest priority level is "silent", and should actually never be used. If 
  2968. you do not want to play the sound you should free the sound channel instead.
  2969.  
  2970. The priority can be varied inside each priority group. For example; soft and 
  2971. quiet instruments/notes should have lower priority than loud and characteristic 
  2972. instruments/notes. You can also alter the priority while the sound is being 
  2973. played. The first part of an emergency sound can for example have higher 
  2974. priority than the rest of the sound.
  2975.  
  2976. Sadly there does not exist any header file that defines the different 
  2977. priorities. In my examples I will therefore use my own constants which are 
  2978. declared like this:
  2979.  
  2980.   Type of sound      Priority
  2981.   ---------------------------
  2982.   SOUND_UNSTOPPABLE    127
  2983.   SOUND_EMERGENCIES     95
  2984.   SOUND_ATTENTION       85
  2985.   SOUND_SPEECH          75
  2986.   SOUND_INFORMATION     60
  2987.   SOUND_MUSIC            0
  2988.   SOUND_EFFECT         -35
  2989.   SOUND_BACKGROUND     -90
  2990.   SOUND_SILENCE       -128
  2991.  
  2992. 4.2.2  ALLOCATING CHANNELS
  2993.  
  2994. There exist four sound channels which each one can convert digital sound data 
  2995. into analogue sound which is sent to a loudspeaker. The first and last sound 
  2996. channel is connected to the left audio port, while the second and third is 
  2997. connected to the right audio port.
  2998.  
  2999. When you are reserving a sound channel you specify which channels you want to 
  3000. use by using a value where the first bit represents the first sound channel, 
  3001. the second bit the second sound channel and so on...
  3002.   Bit:         3     2      1      0
  3003.   -------------------------------------
  3004.   Value:       8     4      2      1
  3005.   Channel:     3     2      1      0
  3006.   Audio port:  Left  Right  Right  Left
  3007.  
  3008. To reserve the first and last channel (both using the left sound channel) you 
  3009. would use the value 9 (1001[bin] = 9[dec]). To reserve the second and third 
  3010. channel (both using the right sound channel) you would use the value 6 
  3011. (0110[bin] = 6[dec]). To reserve all four sound channels set the value to 15 
  3012. (1111[bin] = 15[dec]).
  3013.  
  3014. Since other programs may be running at the same time it may happen that they 
  3015. are already using one or more of the sound channels. If they have lower 
  3016. priority than your program, you will "steal" the sound channel from them. 
  3017. However, if you have the same or lower priority, you are refused any access to 
  3018. that sound channel. 
  3019.  
  3020. The good thing is that you may use an array of allocation values, and if the 
  3021. first value could not be satisfied the audio device will automatically try the 
  3022. next value. If you are lucky there exist a combination of sound channels which 
  3023. is not currently being used.
  3024.  
  3025. If you want to play some sounds in stereo you have to use one sound channel 
  3026. from the right and one from the left port. There exist four possible 
  3027. combinations which satisfy this, and the "allocation array" should therefore 
  3028. look like this:
  3029.  
  3030.   Dec    Bin     Description
  3031.   ---------------------------------------------------
  3032.    3    0011    First left and first right channel.
  3033.    5    0101    First left and second right channel.
  3034.   10    1010    Second left and first right channel.
  3035.   12    1100    Second left and second right channel.
  3036.  
  3037.   UBYTE allocation_array = { 3, 5, 10,12 };
  3038.  
  3039. The audio device will first try all combinations in the allocation array before 
  3040. it will use the priority to "steal"
  3041. the channels for you.
  3042.  
  3043. 4.2.3  CREATE WAVEFORMS
  3044.  
  3045. The audio device can play any type of waveform that is ranging from -128 up to 
  3046. 127. Usually you will use the normal sine, triangle, and square wave forms, but 
  3047. you can equally well play more complex waveforms and even directly sample 
  3048. sound.
  3049.  
  3050. It is important to note that the audio device is a part of the special custom 
  3051. chips in the Amiga and can therefore only access sound data which is located in 
  3052. the "chip memory". The data must also start on a word boundary, and consists of 
  3053. an even number of bytes.
  3054.  
  3055. The more data used for the waveform the more complex waves can be used. The 
  3056. smallest waveforms consists of only two values.
  3057.  
  3058. Example: We will create a sin waveform with 16 values.
  3059.  
  3060.   /* If we are using mathematical functions */
  3061.   /* like sin() we must include this file:  */
  3062.   #include <math.h>
  3063.  
  3064.   /* Define a constant for the number of values in */
  3065.   /* the wave form. It can then easily be changed. */
  3066.   #define SINE_DATA_LENGTH 16
  3067.  
  3068.   /* Declare a pointer to  our sine wave: */
  3069.   BYTE *sine_wave;
  3070.  
  3071.   /* ... */
  3072.  
  3073.   /* Allocate some chip memory for the sine wave: */
  3074.   /* (All memory allocated by AllocMem() will     */
  3075.   /* always start on a word boundary.)            */
  3076.   sine_wave = (BYTE *) AllocMem( SINE_DATA_LENGTH, MEMF_CHIP );
  3077.  
  3078.   /* Have we got the memory? */
  3079.   if( !sine_wave )
  3080.     clean_up( "Not enough memory!" );  
  3081.  
  3082.   /* Initialize the sine waveform: */
  3083.   for( loop = 0; loop < SINE_DATA_LENGTH; loop++ )
  3084.     sine_wave[ loop ] =
  3085.       127 * sin( loop * 2 * PI / SINE_DATA_LENGTH );
  3086.  
  3087. 4.2.4  NOTES AND FREQUENCIES
  3088.  
  3089. On octave consists of 12 notes. Here is a list of the frequencies that 
  3090. represents the notes which are one octave higher than the middle octave on a 
  3091. piano:
  3092.  
  3093.   Note  Frequency
  3094.   ---------------
  3095.    A      880.0
  3096.    A#     932.3
  3097.    B      987.8
  3098.    C     1046.5
  3099.    C#    1108.7
  3100.    D     1174.7
  3101.    D#    1244.5
  3102.    E     1318.5
  3103.    F     1396.9
  3104.    F#    1480.0
  3105.    G     1568.0
  3106.    G#    1661.2 
  3107.  
  3108. To change octave you simply double/half these values. When you double the 
  3109. values you go one octave up, and when you half the values you go one octave 
  3110. down. Example, A = 880, one octave lower A = 440, one octave higher A = 1760, 
  3111. two octaves higher and A = 3520.
  3112.  
  3113. When the audio device should play a waveform you do not specify which frequency 
  3114. should be used. Instead you specify the "period" value which should be used for 
  3115. each sample (value) in the waveform. The period can be calculated with the 
  3116. following formula:
  3117.  
  3118. Period = Clock ticks per second / Frequency / Number of samples 
  3119.   
  3120. The speed of the clock depends on if you have an European (PAL) or American 
  3121. (NTSC) Amiga. On an NTSC machine the clock ticks 3579545 times per second, 
  3122. while on a PAL machine it only ticks 3546895 times per second.
  3123.  
  3124. Example: We want to play the sine waveform we previously made with the 
  3125. frequency 880 (note A). (We are using a European PAL Amiga.)
  3126.   
  3127. period = 3546895 / 880 / SINE_DATA_LENGTH;
  3128.  
  3129. Some extra information: If you have not already noticed it you may get the 
  3130. wrong answer when you are dividing values with each other. The problem is that 
  3131. C is always using the most accurate value in the formula as the base on which 
  3132. it does the calculations. If you multiply an integer value and a float value 
  3133. the C compiler will do the intermediate calculations with float values. If you 
  3134. on the other hand multiply two integers with each other, the C compiler will do 
  3135. the intermediate calculations with integer values.
  3136.  
  3137. The problem is that if you divide two values with each other, and both are 
  3138. integers, the calculations will be preformed with integer values. Since you do 
  3139. not have any decimals you can end up with a completely wrong answer. With 
  3140. multiplications (and addition and subtraction as well) this will never cause 
  3141. any errors.
  3142.  
  3143. When you are dividing two values with each other you should therefore always 
  3144. convert the value (which you divide the other value with) to a float (or double 
  3145. if necessary). Simply use normal casting. The rule is to always put the word 
  3146. "(float)" in front of the bottom value when you are using division. In our 
  3147. example the formula should look like this: (The second float is actually 
  3148. unnecessary, but it does not matter.)
  3149.  
  3150. period = 3546895 / (float) 880 / (float) SINE_DATA_LENGTH;
  3151.  
  3152. 4.2.5  THE AUDIO REQUEST BLOCK
  3153.  
  3154. The audio device is controlled like any other device with help of request 
  3155. blocks. When you are using the audio device you should use the extended 
  3156. "IOAudio" structure which is declared in the header file "devices/audio.h" as 
  3157. this:
  3158.  
  3159. struct IOAudio
  3160. {
  3161.   struct IORequest ioa_Request;
  3162.   WORD ioa_AllocKey;
  3163.   UBYTE *ioa_Data;
  3164.   ULONG ioa_Length;
  3165.   UWORD ioa_Period;
  3166.   UWORD ioa_Volume;
  3167.   UWORD ioa_Cycles;
  3168.   struct Message ioa_WriteMsg;
  3169. };
  3170.  
  3171. ioa_Request:
  3172.     
  3173. The top part of the request block consists as always of an IORequest structure. 
  3174. See below for more information about this structure.
  3175.               
  3176. ioa_AllocKey:
  3177.     
  3178. When you reserve a sound channel this field is given an unique "key" value. 
  3179. Each time you pass the request to the channel this value will be compared with 
  3180. the channel's current key value. If these two values are not the same, the 
  3181. channel has been stolen by some other program with higher priority, and your 
  3182. command is returned with the AUDIO_NOALLOCATION flag set. 
  3183.  
  3184.     
  3185. If you are going to use another request block, and not the one you used to 
  3186. allocate the channel with, you have to copy this key value to the other request 
  3187. block.
  3188.  
  3189. ioa_Data:
  3190.     
  3191. Pointer to the waveform data. Note that the data must be in chip memory, and 
  3192. start on a word boundary.
  3193.  
  3194. ioa_Length:
  3195.     
  3196. If you are going to play a waveform this filed should be given the length 
  3197. (number of samples/values in the wave data).
  3198.  
  3199. ioa_Period:
  3200.     
  3201. This field should be given a period value. The period value is the number of 
  3202. clock ticks that should be used for each sample (value in the waveform). See 
  3203. above for more information about how to calculate the period value. 
  3204.  
  3205. ioa_Volume:
  3206.     
  3207. The volume, which should be set to any value between 0 and 64. The maximum 
  3208. value is 64, and the minimum value is 0 (silent). Many programs are always 
  3209. using the maximum volume, but if you use different volume for different 
  3210. situations you will gain a lot of "depth".
  3211.               
  3212.     
  3213. Imagine an adventure game where most of the sound effects are played rather 
  3214. softly. The user will then use a higher volume on his/her own stereo (or 
  3215. whatever the sound ports are connected to) to compensate for the low volume. No 
  3216. imagine the feeling when a monster appear and you at the same time suddenly 
  3217. increase the volume to maximum!
  3218.  
  3219.     
  3220. Sadly very few programs are using this very effective technique.
  3221.  
  3222. ioa_Cycles:
  3223.     
  3224. Before you play a waveform you should set this field to the number of times the 
  3225. waveform should be played. If you set this field to 0 the waveform will 
  3226. continuously be played until you aborts the command.
  3227.  
  3228. ioa_WriteMsg:
  3229.     
  3230. If you have set the flag "ADIO_WRITEMESSAGE" in the "io_Flag" field, and this 
  3231. request will start a sound, this message will be sent when the sound starts to 
  3232. be played. Remember to give the "mn_ReplyPort" field of the Message structure a 
  3233. pointer to a message port to which the message should be sent.
  3234.  
  3235. The IORequest structure (which is a part of the IOAudiostructure) is defined in 
  3236. the "exec/io.h" header file like this:
  3237.  
  3238. struct IORequest
  3239. {
  3240.   struct Message io_Message;
  3241.   struct Device  *io_Device;
  3242.   struct Unit    *io_Unit;
  3243.   UWORD  io_Command;
  3244.   UBYTE  io_Flags;
  3245.   BYTE   io_Error;
  3246. };
  3247.  
  3248. io_Message:
  3249.     
  3250. The top part consists of a Message structure which will be sent to us when the 
  3251. request has been completed (successfully or not).
  3252.             
  3253.     
  3254. The Message structure is defined in the header file "exec/ports.h" like this:
  3255.             struct Message
  3256.             {
  3257.               struct Node mn_Node;
  3258.               struct MsgPort *mn_ReplyPort;
  3259.               UWORD mn_Length;
  3260.             };
  3261.  
  3262.     
  3263. mn_Node:
  3264.     
  3265. It is only one field of the Node structure that should be used, and it is the 
  3266. "ln_Pri" filed, which should be given this sound's priority value.
  3267.              
  3268.     
  3269. mn_ReplyPort:
  3270.     
  3271. This field should be given a pointer to the message port the reply should be 
  3272. sent to.
  3273.             
  3274.     
  3275. mn_Length:
  3276.     
  3277. This value can be ignored.
  3278.  
  3279. io_Device:
  3280.     
  3281. This field will automatically be initialized when you use the request block 
  3282. together with an OpenDevice() function call. It is simply a pointer to the 
  3283. device which this request block is made for.
  3284.  
  3285. io_Unit:
  3286.     
  3287. This field will automatically be initialized when you reserve an audio channel 
  3288. for this request block.
  3289.  
  3290. io_Command:
  3291.     
  3292. All commands should be set here. The following commands are accepted by the 
  3293. audio device, and may be used: (Will be explained below.)
  3294.  
  3295.     
  3296. The unique audio device commands:
  3297.  
  3298.     
  3299. ADCMD_FREE
  3300.     
  3301. Free a sound channel.
  3302.     
  3303. ADCMD_SETPREC
  3304.     
  3305. Set priority.
  3306.     
  3307. ADCMD_FINISH
  3308.     
  3309. Stop playing the sound.
  3310.     
  3311. ADCMD_PERVOL
  3312.     
  3313. Set period and volume.
  3314.     
  3315. ADCMD_LOCK
  3316.     
  3317. Lock a sound channel.
  3318.     
  3319. ADCMD_WAITCYCLE
  3320.     
  3321. Wait for the cycle to finish.
  3322.     
  3323. ADCMD_ALLOCATE
  3324.     
  3325. Reserve a sound channel.
  3326.  
  3327.     
  3328. The normal commands which are accepted by most of the devices:
  3329.  
  3330.     
  3331. CMD_WRITE
  3332.     
  3333. Play the waveform.
  3334.     
  3335. CMD_READ
  3336.     
  3337. Get a pointer to current req.
  3338.     
  3339. CMD_STOP
  3340.     
  3341. Stop and do not start any sound.
  3342.     
  3343. CMD_START
  3344.     
  3345. Start to play sound again.
  3346.     
  3347. CMD_FLUSH
  3348.     
  3349. Remove all queued requests.
  3350.     
  3351. CMD_RESET
  3352.     
  3353. Reset the audio device.
  3354.  
  3355. io_Flags:
  3356.     
  3357. The following flags may be used:
  3358.  
  3359.     
  3360. IOF_QUICK          If this flag is set no messages will be sent to the reply 
  3361. port. This will speed up the execution, and can be useful if you have to use a 
  3362. lot of request blocks.
  3363.  
  3364.     
  3365. ADIOF_PERVOL       When you start to play a sound you can either use the volume 
  3366. and period values which were used last time, or you may use new values. If you 
  3367. want to use new volume and period values you should set this flag, and also set 
  3368. the new values in this request block. Otherwise the old values will be used.
  3369.  
  3370.     
  3371. ADIOF_SYNCCYCLE    Set this flag if you want that the command should be 
  3372. synchronized with the wave form. If this flag is set, any modifications will 
  3373. first occur when the whole waveform (cycle) have been played.
  3374.  
  3375.     
  3376. ADIOF_NOWAIT       When you try to reserve a sound channel (with 
  3377. ADCMD_ALLOCATE) and the audio device can not find any channel for you, it will 
  3378. continue to try until it succeeds. The request will firs be returned when the 
  3379. request has successfully been completed.
  3380.                                  
  3381.     
  3382. If you do not want to wait for the request to be completed you can set this 
  3383. flag. If the audio device can not find any sound channel it will immediately 
  3384. return the request with the "ADIOERR_ALLOCFAILED" error flag set. 
  3385.  
  3386.     
  3387. ADIOF_WRITEMESSAGE Normally you will only receive a message at your reply port 
  3388. when a request has been completed. It may however sometimes be necessary to 
  3389. know when an audio request have started.
  3390.                                  
  3391.     
  3392. If you set this flag the message at the end of the request ("ioa_WriteMsg") 
  3393. will be sent. Remember to give the message structure a pointer to a reply port 
  3394. if you are using this flag.
  3395.  
  3396. io_Error:
  3397.     
  3398. If the request can not successfully be executed by the audio device, it will be 
  3399. returned with one of the following error flags:
  3400.  
  3401.     
  3402. ADIOERR_NOALLOCATION  When you reserve a sound channel you are given a "key" 
  3403. value (set in the "ioa_AllocKey" field). Every time you send a request this key 
  3404. value will be compared to the sound channel's current key value. If these two 
  3405. values do not match the channel have been "stolen" by some other program. The 
  3406. request will then be returned with this error flag set.
  3407.                                     
  3408.     
  3409. Note that you do not have to free a sound channel that has been stolen from 
  3410. you.
  3411.  
  3412.     
  3413. ADIOERR_ALLOCFAILED   When you try to reserve a sound channel it may happen 
  3414. that all desired channels are occupied. If the audio device can not find a 
  3415. channel it will normally continue to try until it found one. However, if you 
  3416. have set the "ADIOF_NOWAIT" flag the request will immediately be returned if no 
  3417. sound channels could be allocated, and this error flag is then set.
  3418.                                     
  3419.     
  3420. Remember to always check if you have received the channel or not if you are 
  3421. using the "ADIOF_NOWAIT" flag. 
  3422.  
  3423.     
  3424. ADIOERR_CHANNELSTOLEN If you have "locked" a channel, and another program tries 
  3425. to steal it, the lock request will be returned to you with this flag set.
  3426.                                     
  3427.     
  3428. If the lock request is returned to you with this flag set you should as fast as 
  3429. possible clean up after you and unlock the channel. It is first when you free 
  3430. the locked channel the other program gets it.
  3431.  
  3432.     
  3433. NOTE! You must free the channel as soon as possible! The other program has 
  3434. higher priority, but since you have locked the channel it can not get it.
  3435.  
  3436. 4.2.6  OPEN THE AUDIO DEVICE  
  3437.  
  3438. The audio device is very similar to other devices. You open a message port 
  3439. through which the audio device can reply to you. You then allocate the request 
  3440. block(s), and finally open the device.
  3441.  
  3442. 1. Open a message port. Since it is only our task and the audio device that 
  3443. will use the message port, we do not need to make it "public", therefore no 
  3444. name. Priority should as usual be set to 0, normal priority. (Do not mix up 
  3445. this priority for the message port with the priority for the sound channel, 
  3446. which will later be set in the request block.)
  3447.  
  3448.        struct MsgPort *replymp;
  3449.  
  3450.        replymp = (struct MsgPort *)
  3451.          CreatePort( NULL, 0 );
  3452.  
  3453.        if( !replymp )
  3454.          clean_up( "Could not create the reply port!" );
  3455.  
  3456. 2. Allocate a request block of type IOAudio structure. The IOAudio structure is 
  3457. an extended version of the normal request block, and should therefore be 
  3458. allocated with help of the CreateExtIO() function with the size set to sizeof( 
  3459. struct IOAudio ).
  3460.  
  3461.        struct IOAudio *audio_req;
  3462.  
  3463.        audio_req = (struct IOAudio *)
  3464.          CreateExtIO( replymp, sizeof( struct IOAudio ) );
  3465.  
  3466.        if( !audio_req )
  3467.          clean_up( "Not enough memory!" );
  3468.  
  3469. 3. Once the message port and the request block have successfully been created 
  3470. you can "open" (gain access to) the audio device.
  3471.  
  3472.        error = OpenDevice( AUDIONAME, 0, audio_req, 0 );
  3473.  
  3474.        if( error )
  3475.          clean_up( "Could not open the Audio Device!" );
  3476.  
  3477.  
  3478.     
  3479. If you want you can reserve sound channels at the same time as you open the 
  3480. device. You must then:
  3481.  
  3482. (a) Set the sound priority. The top part of the request block, the IORequest 
  3483. structure (ioa_Request) contains a Message structure ("io_Message") which 
  3484. contains a Node structure (mn_Node) which finally contains a "ln_Pri" field. 
  3485. (Piece of cake.) It is in this field you set the sound priority.
  3486.  
  3487. (b) Give the "ioa_Data" field a pointer to an array were the desired channel 
  3488. combinations are listed. This array of desired channels are usually called "the 
  3489. allocation array".
  3490.          
  3491. (c) Set the length of the allocation array in the "ioa_Length" field.
  3492.          
  3493. If the "ioa_Length" is not zero when you try to open the device, the audio 
  3494. device will also try to reserve the desired channel(s). If the audio device can 
  3495. not find any sound channels free, it will immediately (regardless if you have 
  3496. set the "ADIOF_NOWAIT" flag or not) return with the "ADIOERR_ALLOCFAILED" error 
  3497. message. 
  3498.       
  3499. 4.2.7  RESERVE CHANNELS
  3500.  
  3501. If you have not reserved any channel(s) when you opened the audio device, or 
  3502. you want to reserve even more channels, you should use the "ADCMD_ALLOCATE" 
  3503. command. The follwing things must be done:
  3504.  
  3505. 1. Set the "ADCMD_ALLOCATE" flag in the "io_Command" field of the IORequest 
  3506. structure.
  3507.  
  3508.        audio_req->ioa_Request.io_Command = ADCMD_ALLOCATE;
  3509.  
  3510. 2. Set the sound priority. The top part of the request block, the IORequest 
  3511. structure (ioa_Request) contains a Message structure ("io_Message") which 
  3512. contains a Node structure (mn_Node) which finally contains a "ln_Pri" field. It 
  3513. is in this field you set the sound priority. Be careful so you set the correct 
  3514. sound priority. 
  3515.  
  3516.        audio_req->ioa_Request.io_Message.mn_Node.ln_Pri = SOUND_EFFECT;
  3517.  
  3518. 3. Normally will the request first be returned when the audio device has 
  3519. successfully reserved the sound channel(s). If you set the "ADIOF_NOWAIT" flag 
  3520. in the "io_Flags" field of the IORequest structure, the request will 
  3521. immediately be returned, successfully or not. You can examine the "io_Error" 
  3522. field of the IORequest structure to see if the request was successful or not. 
  3523. If no channels could be reserved (allocated) the "ADIOERR_ALLOCFAILED" error 
  3524. flag is set.
  3525.  
  3526.        audio_req->ioa_Request.io_Flags = ADIOF_NOWAIT;
  3527.  
  3528. 4. Give the "ioa_Data" field a pointer to an array were the desired channel 
  3529. combinations are listed.
  3530.  
  3531.        audio_req->ioa_Data = allocation_array;
  3532.   
  3533. 5. Set the length of the allocation array in the "ioa_Length" field.
  3534.  
  3535.        audio_req->ioa_Length = sizeof( allocation_array );
  3536.          
  3537. 6. The audio request block has now been properly initialized, and it should now 
  3538. be sent to the audio device. Normally you use the DoIO() and SendIO() commands, 
  3539. but they may not be used when you are allocating channels. Instead you must use 
  3540. the low level function BeginIO(), which is very similar to SendIO().
  3541.      
  3542. The reason why you may not use the DoIO() and SendIO() is that they will erase 
  3543. some parts of the request block that may not be altered when you are using the 
  3544. audio device.
  3545.      
  3546. Since BeginIO() is an asynchronous command, it will immediately return the 
  3547. control to the program, you have to wait for the request to be executed. Simply 
  3548. use the WaitIO() function which will put our program to sleep while we are 
  3549. waiting.
  3550.      
  3551. WaitIO() will return 0 when the request has successfully been executed, else a 
  3552. non zero value is returned.
  3553.  
  3554.  
  3555.        BeginIO( audio_req );
  3556.  
  3557.        error = WaitIO( audio_req );
  3558.  
  3559.        if( error )
  3560.          clean_up( "No channels!" );
  3561.  
  3562. After you have successfully allocated a channel you can check which channel(s) 
  3563. you received by looking at the "io_Unit" field of the IORequest structure. 
  3564. (Note that normally the "io_Unit" field of a IORequest structure contains a 
  3565. pointer to a Unit structure. However, when you are using the audio device this 
  3566. field is used to store an integer value, where the first four bits are used to 
  3567. identify which channels are reserved. As before, bit zero represents channel 0 
  3568. (left), bit one channel 1 (right), bit two channel 2 (right) and finally bit 
  3569. three represents channel 3 (left).
  3570.  
  3571.        channel = (UBYTE) audio_req->ioa_Request.io_Unit;
  3572.  
  3573.        if( channel & 1 )
  3574.          printf( "First left channel!\n" );
  3575.  
  3576.        if( channel & 2 )
  3577.          printf( "First right channel!\n" );
  3578.  
  3579.        if( channel & 4 )
  3580.          printf( "Second right channel!\n" );
  3581.  
  3582.        if( channel & 8 )
  3583.          printf( "Second left channel!\n" );
  3584.  
  3585. 4.2.8  LOCK CHANNELS
  3586.  
  3587. After you have reserved a channel you may start to use it. When you reserved 
  3588. the sound channel was the "ioa_AllocKey" given an unique key number. This key 
  3589. number will be compared with the sound channels current key number, and if they 
  3590. do not match, the channel(s) has/have been stolen and your request is returned 
  3591. with the error flag "ADIOERR_NOALLOCATION" set.
  3592.  
  3593. If you want to use several request blocks for the same  channel(s) you must 
  3594. therefore copy the key value to all request blocks.
  3595.  
  3596. When you are using the request block to send commands the key value will always 
  3597. be checked before any change is made. It can however sometimes be necessary to 
  3598. skip the request block and directly modify the hardware registers. Using 
  3599. request blocks take some time, and if you have to make changes several times 
  3600. each second it may not be fast enough. You must then directly modify the sound 
  3601. registers.
  3602.  
  3603. Normally you are not allowed to hit the hardware like this, but when you are 
  3604. using the audio device you are allowed to do it. Of course, if you can manage 
  3605. with the request blocks you should use them.
  3606.  
  3607. If you are using the hardware registers you may get into trouble if you are not 
  3608. careful. If some other program has stolen the channel(s) from you and you use 
  3609. the hardware registers, you will not receive any error messages. You can now 
  3610. end up with modifying the other programs sounds, and this is not to be 
  3611. recommended!
  3612.  
  3613. Before you may use the hardware registers you must therefore "lock" the 
  3614. channel(s). To lock a channel you have to use a separate request block. This 
  3615. request block is allocated and pre-initialized as usual with help of the 
  3616. CreateExtIO() function: (We can of course use a different message port if you 
  3617. want to separate all lock messages, but it is usually easier to use only one 
  3618. message port for all audio messages.)
  3619.  
  3620.   struct IOAudio *audio_lock;
  3621.  
  3622.   audio_lock = (struct IOAudio *)
  3623.     CreateExtIO( replymp, sizeof( struct IOAudio ) );
  3624.  
  3625.   if( !audio_lock )
  3626.     clean_up( "Not enough memory!" );
  3627.  
  3628. The lock should then have the following fields set:
  3629.  
  3630. 1. The "ADCMD_LOCK" flag in the io_Command field of the IORequest structure 
  3631. must be set.
  3632.  
  3633.       audio_lock->ioa_Request.io_Command = ADCMD_LOCK;
  3634.  
  3635. 2. The lock must be linked to the already opened audio device. The address to 
  3636. the device can be found in the "io_Device" field of the IORequest structure.
  3637.  
  3638.       audio_lock->ioa_Request.io_Device = audio_req->ioa_Request.io_Device;
  3639.  
  3640. 3. You must tell the device which channels you want to lock. Normally you will 
  3641. try to lock the channels you previously have reserved, and thus you can find 
  3642. the channel value in the "io_Unit" field of the IORequest block.
  3643.  
  3644.       audio_lock->ioa_Request.io_Unit = audio_req->ioa_Request.io_Unit;
  3645.  
  3646. 4. Since you are going to use a new request block for some channels you have 
  3647. previously reserved with another request block, you must copy the key value:
  3648.  
  3649.      audio_lock->ioa_AllocKey = audio_req->ioa_AllocKey;
  3650.  
  3651. Once the lock request has been initialized it should be send by calling the 
  3652. BeginIO() function.
  3653.  
  3654. BeginIO( audio_lock );
  3655.  
  3656. Once you have lock the channel(s) the request block will first be sent back 
  3657. when some other programs tries to steal the sound channel(s) or when you free 
  3658. the channel(s). If some other program wants the channel(s) and has higher 
  3659. priority, the lock will be returned with the error flag "ADIOERR_CHANNELSTOLEN" 
  3660. set. The channel has not yet been stolen, but you MUST immediately clean up all 
  3661. necessary things and free the channel(s) as soon as possible so the other 
  3662. program can get them. You may NOT keep the channels once the lock has been 
  3663. returned!
  3664.  
  3665. While you are cleaning up and freeing the channels the other program is put to 
  3666. sleep and does not know what is happening. If you are not fast enough the other 
  3667. program will continue to "sleep" although it has higher priority and should 
  3668. therefore immediately get the channels if you have not locked them. You must 
  3669. therefore free the channels as fast as possible.
  3670.  
  3671. You may of course not modify the hardware registers any more after you have 
  3672. deallocated the audio channel(s).
  3673.  
  3674. You can use the function CheckIO() to see if the lock has been sent back. See 
  3675. below for more information about how to free a sound channel.
  3676.   /* Has the lock been sent back? */
  3677.   if( CheckIO( audio_lock ) )
  3678.   {
  3679.     /* Another programs what our channel(s)! */
  3680.  
  3681.     /* Clean up. Reset any hardware */
  3682.     /* registers etc, if necessary. */
  3683.  
  3684.     /* Free the channel: */
  3685.     audio_req->ioa_Request.io_Command = ADCMD_FREE;
  3686.  
  3687.     /* Send the request: (We may use the DoIO() function */
  3688.     /* when we deallocate a channel.)                    */
  3689.     DoIO( audio_req );
  3690.   }
  3691.  
  3692. 4.3  USE THE AUDIO DEVICE
  3693.  
  3694. Once you have declared a request block and successfully opened the audio device 
  3695. you may start to use it. When you are playing sounds you may either use the 
  3696. request block to issue the sound commands, or you can directly modify the 
  3697. hardware registers. The first method is cleaner, but the second method is much 
  3698. faster and may sometimes be necessary if you intend to issue many commands.
  3699.  
  3700. 4.3.1  PLAY SOUNDS
  3701.  
  3702. To play some sound you have to do the following things:
  3703.  
  3704. 1. Give the "ioa_Data" field of the request block a pointer to a waveform you 
  3705. want to play. (The waveform data must be located in chip memory, and start on 
  3706. an even byte address.)
  3707.  
  3708.        audio_req->ioa_Data = sine_wave;
  3709.  
  3710. 2. Set the length of the waveform in the "ioa_Length" field. (Must be an even 
  3711. number of bytes.)
  3712.  
  3713.        audio_req->ioa_Length = SINE_DATA_LENGTH;
  3714.  
  3715. 3. Set the number of times (cycles) the waveform should be played in the 
  3716. "ioa_Cycles" field. If you want to play the waveform continuously until you 
  3717. abort the request you should set the field to 0.
  3718.  
  3719.        audio_req->ioa_Cycles = 3;
  3720.  
  3721. 4. Set the command "CMD_WRITE" flag in the "io_Command" field which tells the 
  3722. device that you want to play a tune.
  3723.  
  3724.        audio_req->ioa_Request.io_Command = CMD_WRITE;
  3725.  
  3726. 5. If you want to use a specific volume and period values you must set the 
  3727. "ADIOF_PERVOL" flag in the "io_Flags" field.  The values in the "ioa_Volume" 
  3728. and "ioa_Period" fields will then be used, else the previous volume and period 
  3729. values will be used.
  3730.      
  3731.        audio_req->ioa_Request.io_Flags = ADIOF_PERVOL;
  3732.        audio_req->ioa_Volume = 32;
  3733.        audio_req->ioa_Period = 3546895 / 880 / SINE_DATA_LENGTH;
  3734. Once all desired values have been set you send the request to the audio device, 
  3735. by calling the BeginIO() function. If you want to wait for the sound to be 
  3736. completed you can use the WaitIO() function. Note that you should NEVER try to 
  3737. wait for a request that has not been started, or if the "ioa_Cycles" field has 
  3738. been set to 0 (forever). If the request already has been completed the WaitIO() 
  3739. function will immediately return.
  3740.  
  3741.     BeginIO( audio_req );
  3742.  
  3743.     error = WaitIO( audio_req );
  3744.  
  3745.     if( error )
  3746.       clean_up( "Error while playing the sound!" );
  3747.  
  3748. 4.3.2  USE SEVERAL REQUEST BLOCKS
  3749.  
  3750. Remember that you may not modify the request block once it has been sent, by 
  3751. calling BeginIO(), and have not yet been completed. Once the request has been 
  3752. completed you of course start to modify it again. If you want to play several 
  3753. notes at the same times in different sound channels, you may therefore have to 
  3754. use several request blocks.
  3755.  
  3756. If you want to use some of the audio commands that is changing the sound which 
  3757. is currently being played you also have to a use separate request blocks. In 
  3758. many programs you will see four request blocks where each one handles one audio 
  3759. channel. Very often there also exist one or more extra request blocks that are 
  3760. used to modify sounds which are currently being played.
  3761.  
  3762. If you have reserved a sound channel with a request block you can not use 
  3763. another request block to play sounds on it if you do not first copy some 
  3764. important values to the new request block.
  3765.  
  3766. When you reserve a channel the request block is given a unique key value, and 
  3767. if this key value does not match with the sound channel's current key value the 
  3768. request will fail. Before you may use a new request block you must therefore 
  3769. first copy the "ioa_AllocKey" value to the new request.
  3770.  
  3771. You must also copy the pointer to the audio device from the old request to the 
  3772. new one. The unit number and all stuff in the Message structure must also be 
  3773. copied. In the end, it is actually easier to copy the whole request block, even 
  3774. if some fields does not have to be copied.
  3775.  
  3776. Here is a small demonstration on how to copy all values in a request block to a 
  3777. new one. The "audio_req" is a pointer to an already initialized audio request 
  3778. block, where "change_req" is a new and uninitialized request block.
  3779.  
  3780.   /* Source and destination pointers: */
  3781.   BYTE *first_ptr;
  3782.   BYTE *second_ptr;
  3783.  
  3784.  
  3785.   /* Allocate request blocks, open audio device, */
  3786.   /* reserve channels, etc...                    */
  3787.  
  3788.  
  3789.   /* Copy the first request block to the */
  3790.   /* second one: (byte by byte)          */
  3791.  
  3792.   /* Get the start addresses of both request blocks: */
  3793.   first_ptr = (BYTE *) audio_req;
  3794.   second_ptr = (BYTE *) change_req;
  3795.  
  3796.   /* Copy byte by byte: */
  3797.   for( loop = 0; loop < sizeof( struct IOAudio ); loop++ )
  3798.   {
  3799.     /* Copy: */
  3800.     *second_ptr = *first_ptr;
  3801.     
  3802.     /* Next byte: */
  3803.     first_ptr++;
  3804.     second_ptr++;
  3805.   }
  3806.  
  3807. 4.3.2  PLAY DOUBLE BUFFERED SOUNDS
  3808.  
  3809. If you play several sounds after each other you will hear a "click" between the 
  3810. sounds. The reason why you hear this click is that once a sound has been 
  3811. completed it takes some time for your program to initialize a new request and 
  3812. send it to the audio device. Although this delay is very short it is long 
  3813. enough to be noticed, especially if several programs are running at the same 
  3814. time.
  3815.  
  3816. Luckily there is a solution for this problem. Before the first sound has been 
  3817. complete you should send the next sound request. When the first sound has 
  3818. stopped the second sound is already available for the audio device, and it will 
  3819. immediately be played. Because of this, there is such a short interrupt between 
  3820. the sounds that you will not hear any annoying clicks.
  3821.  
  3822. This technique of sending the next sound when the first one  is still being 
  3823. played is usually called "double buffering", and can in many ways be compared 
  3824. with "double buffered displays" which is described in the manual "Graphics", 
  3825. chapter "Graphical Tricks".
  3826.  
  3827. 1. Prepare the first request block and send it to the audio device.
  3828.  
  3829. 2. Prepare the second request block, and send it to the audio device.
  3830.  
  3831. 3. Wait for the first request block to be completed. When the request block is 
  3832. returned you prepare it again for a new sound, and send it to the audio device.
  3833.  
  3834. 4. Wait for the second request block to be completed. When the request block is 
  3835. returned you prepare it again for a new sound, and send it to the audio device.
  3836.  
  3837. 5. Jump back to step 3 until all sounds have been played.
  3838.  
  3839. 6. Wait for both requests to be completed, and quit.
  3840.  
  3841. 4.3.3  MODIFY THE HARDWARE REGISTERS
  3842.  
  3843. Usually you should never try to modify hardware registers directly but instead 
  3844. use special functions that does it for you. However, when you are using the 
  3845. audio device you may alter the hardware registers of the sound channels. 
  3846. Modifying hardware registers directly is much faster than using functions that 
  3847. does it for you, and when playing sounds speed can sometimes be crucial.
  3848.  
  3849. Before you may alter any of the sound registers you must first lock the 
  3850. channel(s)! When you modify hardware registers no one is no checking that you 
  3851. really own the channel(s). If you have reserved a channel and not locked it, 
  3852. some other program may steal it and you would not notice that. You could then 
  3853. end up altering the other programs sound channel(s), which is rather unpolite 
  3854. not to say dangerous.
  3855.  
  3856. To use the hardware registers you have to declare an external array of 
  3857. "AudChannel" structures. The AudChannel structure is defined in the Custom 
  3858. structure which is declared in the header file "hardware/custom.h". The whole 
  3859. Custom structure is automatically initialized by the Amiga. The AudChannel 
  3860. array, which must be called "aud" is also initialized.
  3861.  
  3862. The AudChannel structure is defined like this: (Declared in header file 
  3863. "hardware/custom.h")
  3864.  
  3865. struct AudChannel
  3866. {
  3867.   UWORD *ac_ptr;
  3868.   UWORD ac_len;
  3869.   UWORD ac_per;
  3870.   UWORD ac_vol;
  3871.   UWORD ac_dat;
  3872.   UWORD ac_pad[2];
  3873. } aud[4];
  3874.  
  3875. ac_ptr:
  3876.     
  3877. Pointer to the waveform data.
  3878.  
  3879. ac_len:
  3880.     
  3881. The length of the waveform.
  3882.  
  3883. ac_per:
  3884.     
  3885. The period value.
  3886.  
  3887. ac_vol:
  3888.     
  3889. The volume.
  3890.  
  3891. ac_dat:
  3892.     
  3893. This field contains the first two bytes which will be played. The DMA channels 
  3894. will automatically move data from the waveform to this field, two bytes each 
  3895. time, when needed. You can alter these values directly, but this will take 
  3896. longer time than letting the DMA fetch the sound automatically, so it is not 
  3897. recommended.
  3898.  
  3899. ac_pad[2]:
  3900.     
  3901. Unused areas, should not be used.
  3902.  
  3903. When you declare the "aud" array you probably have to use the keyword "far" 
  3904. since the hardware data is most certainly far away from your program. Remember 
  3905. that the aud array has been declared and initialized by the Amiga itself, and 
  3906. you should therefore declare it as an external array. Example:
  3907.  
  3908. extern struct AudChannel far aud[];
  3909.  
  3910. To use the hardware audio channels you must also declare an external UWORD 
  3911. which must be called "dmacon". This variable is also automatically initialized 
  3912. by the Amiga. Example:
  3913.  
  3914. extern UWORD far dmacon;
  3915.  
  3916. This "dmacon" variable controls the DMA channels. A DMA channel can be 
  3917. described as a data cable where data can be sent without disturbing the main 
  3918. processor. It is because of the DMA (DMA = "Direct Memory Access") you can play 
  3919. sounds at the same time as your program can use the main processor to do 
  3920. something else.
  3921.  
  3922. It is this variable which controls all "DMA writing", and must therefore be 
  3923. used when you want to play a sound. To play a sound on one of the audio 
  3924. channels you have to give it a value where the leftmost bit is set (the 
  3925. DMAF_SETCLR constant) and the bit which correspond to the audio channel you 
  3926. want to start playing. Bit one represents the first audio channel, bit two the 
  3927. second audio channel and so on. You should however not use the direct values 
  3928. but instead the constants which are declared in the "hardware/dmabits.h" header 
  3929. file.
  3930.  
  3931. DMAF_SETCLR
  3932.     
  3933. This flag should always be set together with the channel(s) you want to be 
  3934. played.
  3935.   
  3936. DMAF_AUD0
  3937.     
  3938. First left audio channel.
  3939.  
  3940. DMAF_AUD1
  3941.     
  3942. First right audio channel.
  3943.  
  3944. DMAF_AUD2
  3945.     
  3946. Second right audio channel.
  3947.  
  3948. DMAF_AUD3
  3949.     
  3950. Second left audio channel.
  3951.  
  3952. If you want to use several audio channels you simple set all desired channel 
  3953. flags separated with the binary OR operator "|". To start all audio channels do 
  3954. like this: (You must of course first have reserved and locked them before you 
  3955. may do this!)
  3956.  
  3957. dmacon = DMAF_SETCLR|DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3;
  3958.  
  3959. Since you can now directly alter the volume, period, and waveform by directly 
  3960. writing to the hardware the changes will be very simple and quick. Just 
  3961. remember to now and then check if your lock has been returned, and if so clean 
  3962. up after you and free the channel(s) as soon as possible. Do not be greedy and 
  3963. ignore the lock, too many programs do that which is very irritating!
  3964.  
  3965. 4.4  CLEAN UP AFTERWARDS
  3966.  
  3967. When your program terminates you have to clean up after yourself, as usual. It 
  3968. is important that you do not forget to do this since other programs are denied 
  3969. any access to the audio channels and memory you forget to deallocate. Remember 
  3970. that your programs must also be able to clean up after itself if it suddenly 
  3971. has to terminate because of some error.
  3972.  
  3973. 4.4.1  UNLOCK CHANNELS
  3974.  
  3975. All audio channels that you have reserved must be given back to the system 
  3976. before your program may terminate. If you have locked a channel and another 
  3977. programs wants it, you must return it as soon as possible. However, if you have 
  3978. not locked the channel and another program has stolen it you do not have to 
  3979. free it.
  3980.  
  3981. To free one or more audio channels you simply send a request with the 
  3982. "ADCMD_FREE" command flag set. All channels which are selected in the "io_Unit" 
  3983. field of the request block will be resetted and given back to the system. Any 
  3984. locks on the channels will be removed.
  3985.  
  3986. If some other program has stolen the channel(s) from you the request will be 
  3987. returned with the error message  "ADIOERR_NOALLOCATION". You do not have to do 
  3988. anything with channel(s) which has/have been stolen from you. It is the other 
  3989. program's duty to clean it/them up.
  3990.  
  3991. When you want to free a channel you may use the DoIO()  function, instead of 
  3992. BeginIO(). Normally you should only use the BeginIO() function when working 
  3993. with the audio device, but when you free channel(s) the DoIO() will work 
  3994. equally well.
  3995.  
  3996.  
  3997. audio_req->ioa_Request.io_Command = ADCMD_FREE;
  3998.  
  3999. DoIO( audio_req );
  4000.  
  4001. 4.4.2  REMOVE ALL MESSAGES AND CLOSE THE REPLY PORT
  4002.  
  4003. You must close all message ports you have opened. Just remember to remove all 
  4004. messages before you close it. A short while loop that removes the messages 
  4005. until it can not find any more is usually enough.
  4006.  
  4007.   while( GetMsg( replymp ) )
  4008.     printf( "Collected a message at the reply port.\n" );
  4009.  
  4010.   DeletePort( replymp);
  4011.  
  4012. 4.4.3  CLOSE THE DEVICE
  4013.  
  4014. The audio device itself must of course also be closed if you have opened it. If 
  4015. you have open it several times with different request blocks you have to close 
  4016. all of them.
  4017.  
  4018.   CloseDevice( audio_req );
  4019.  
  4020.  
  4021. 4.4.4  DEALLOCATE THE REQUEST BLOCKS
  4022.  
  4023. All audio request blocks you have created must be deallocated with help of the 
  4024. DeleteExtIO() function. The size should be set to the size of an IOAudio 
  4025. structure. Just remember to close the device before you deallocate the request 
  4026. block.
  4027.  
  4028.   DeleteExtIO( audio_req, sizeof( struct IOAudio ) );
  4029.  
  4030. 4.4.5  DEALLOCATE SOUND BUFFERS
  4031.  
  4032. If you have allocated a sound buffer to store the waveform in you must also 
  4033. deallocate it. Remember to deallocate the same amount of memory as when you 
  4034. allocated it.
  4035.  
  4036.   FreeMem( square_wave, SINE_DATA_LENGTH );
  4037.  
  4038. 4.5  AUDIO DEVICE COMMANDS
  4039.  
  4040. The audio device is controlled by set of commands. We have looked at the 
  4041. ADCMD_ALLOCATE, ADCMD_FREE, and CMD_WRITE commands. Most things can be managed 
  4042. with these commands, but there exist a set of other commands that may sometimes 
  4043. be needed. These new commands can be divided into two groups. The first group 
  4044. consists of the general device commands which can be used on most devices. The 
  4045. second group consists of a set special commands that only the audio device can 
  4046. understand and handle.
  4047.  
  4048. 4.5.1  GENERAL DEVICE COMMANDS
  4049.  
  4050. Here is the complete list of general device commands, together with a short 
  4051. description:
  4052.  
  4053.  
  4054. CMD_WRITE
  4055.     
  4056. Start to play a sound on one channel.
  4057. CMD_READ
  4058.     
  4059. Check which request is currently playing.
  4060. CMD_STOP
  4061.     
  4062. Temporarily stop all audio requests.
  4063. CMD_START
  4064.     
  4065. Restart the previously stopped audio requests.
  4066. CMD_FLUSH
  4067.     
  4068. Remove all queued requests.
  4069. CMD_RESET
  4070.     
  4071. Reset the audio device.
  4072.  
  4073. 4.5.1.1  CMD_WRITE
  4074.  
  4075. The CMD_WRITE command is used when you want to start to play a sound on a 
  4076. specific sound channel. Note that it can only start one sound channel.
  4077.  
  4078. This command has already been explained, so see previous sections for more 
  4079. information.
  4080.  
  4081. 4.5.1.2  CMD_READ
  4082.  
  4083. The CMD_READ command is used when you want to get the address of the request 
  4084. which is currently being used. You specify a sound channel you want to check by 
  4085. setting the corresponding bit in the io_Unit field. If a sound is currently 
  4086. being played on that channel the "io_Data" field of our request block will 
  4087. contain the address of the other request which is currently playing the sound. 
  4088. If no sound is being played the "io_Data" field is set to NULL.
  4089.  
  4090. This command is useful if you have sent a lot of requests to the audio device 
  4091. and you want to see which request is currently being played. Note that you must 
  4092. copy the sound channels key value to your request before you can check it. The 
  4093. key value can be found in the "ioa_AllocKey" field of the request which 
  4094. reserved the channel. If your request block's "ioa_AllocKey" value does not 
  4095. match the sound channel's current key value the ADIOERR_NOALLOCATION error 
  4096. message is returned.
  4097.  
  4098. 4.5.1.3  CMD_STOP
  4099.  
  4100. The CMD_STOP command will temporarily stop all requests for the specified 
  4101. channel(s). All requests will be queued, and first processed when you later 
  4102. restart the channel(s) by using the CMD_START command. The "io_Unit" field of 
  4103. the request specifies which channels should be stopped. Remember to copy the 
  4104. key value from the request block that reserved the sound channel(s). If your 
  4105. request block's "ioa_AllocKey" value does not match the sound channel's current 
  4106. key value the ADIOERR_NOALLOCATION error message is returned.
  4107.  
  4108. 4.5.1.4  CMD_START
  4109.  
  4110. The CMD_START command should be used when you want to restart one or more 
  4111. f\previously stopped sound channel(s). The "io_Unit" field of the request 
  4112. specifies which channels that should be restarted. Remember to copy the key 
  4113. value from the request block that reserved the sound channel(s). If your 
  4114. request block's "ioa_AllocKey" value does not match the sound channel's current 
  4115. key value the ADIOERR_NOALLOCATION error message is returned.
  4116.  
  4117. 4.5.1.5  CMD_FLUSH
  4118.  
  4119. The CMD_FLUSH command will remove all currently queued commands from the 
  4120. specified channels. Remember to copy the key value from the request block that 
  4121. reserved the sound channel(s). If your request block's "ioa_AllocKey" value 
  4122. does not match the sound channel's current key value the ADIOERR_NOALLOCATION 
  4123. error message is returned.
  4124.  
  4125.  
  4126. 4.5.1.6  CMD_RESET
  4127.  
  4128. The CMD_RESET command will reset all specified channels. All requests currently 
  4129. queued will be removed, the audio hardware registers are cleared, and if the 
  4130. channel has been stopped it is restarted. Remember to copy the key value from 
  4131. the request block that reserved the sound channel(s). If your request block's 
  4132. "ioa_AllocKey" value does not match the sound channel's current key value the 
  4133. ADIOERR_NOALLOCATION error message is returned.
  4134.  
  4135. 4.5.2  SPECIAL AUDIO DEVICE COMMANDS
  4136.  
  4137. Here is the complete list of special audio device commands. These commands can 
  4138. only be used with the audio device.
  4139.  
  4140. ADCMD_ALLOCATE
  4141.     
  4142. Tries to reserve one or more audio channels.
  4143. ADCMD_FREE
  4144.     
  4145. Frees one or more audio channels.
  4146. ADCMD_SETPREC
  4147.     
  4148. Change the priority of a sound.
  4149. ADCMD_FINISH
  4150.     
  4151. Finish (aborts) a sound.
  4152. ADCMD_PERVOL
  4153.     
  4154. Change the volume and period of a sound.
  4155. ADCMD_LOCK
  4156.     
  4157. Will lock audio one or more channel(s).
  4158. ADCMD_WAITCYCLE
  4159.     
  4160. Waits for a cycle to be completed.
  4161.  
  4162. 4.5.1.1  ADCMD_ALLOCATE
  4163.  
  4164. The ADCMD_ALLOCATE command will reserve one or more audio channels. This 
  4165. command has already been discussed, so see previous sections for more 
  4166. information about this command.
  4167.  
  4168. 4.5.1.2  ADCMD_FREE
  4169.  
  4170. The ADCMD_FREE command will free one or more audio channels that has(have) 
  4171. previously been stolen. This command has already been discussed, so see 
  4172. previous sections for more information about this command.
  4173.  
  4174. 4.5.1.3  ADCMD_SETPREC
  4175.  
  4176. The ADCMD_SETPREC command allows you to change the priority of a sound channel 
  4177. that has previously been reserved. You may for example want to use a very high 
  4178. priority at the beginning of the sound, and later decrease it. Remember to copy 
  4179. the key value from the request block that reserved the sound channel(s). If 
  4180. your request block's "ioa_AllocKey" value does not match the sound channel's 
  4181. current key value the ADIOERR_NOALLOCATION error message is returned.
  4182.  
  4183. 4.5.1.4  ADCMD_FINISH
  4184.  
  4185. The ADCMD_FINISH command will finish an already started sound. This may be 
  4186. needed if you have set a very high cycle value, or you are playing a sound "for 
  4187. ever" (the ioa_Cycle field set to zero.) This command has the same effect as 
  4188. calling the AbortIO() function. Both options are equally good, but the 
  4189. ADCMD_COMMAND looks a bit "cleaner" (at least some C programmers think so).
  4190.  
  4191. Remember to copy the key value from the request block that reserved the sound 
  4192. channel(s). If your request block's "ioa_AllocKey" value does not match the 
  4193. sound channel's current key value the ADIOERR_NOALLOCATION error message is 
  4194. returned.
  4195.  
  4196.  
  4197. 4.5.1.5  ADCMD_PERVOL
  4198.  
  4199. The ADCMD_PERVOL command can be useful if you want to change the period and/or 
  4200. volume value(s) of a sound which is already playing. Just remember to set the 
  4201. ADIOF_PERVOL flag in the "io_Flags" field, or else your command will have no 
  4202. effect. Remember to also set the new volume and period values you want to be 
  4203. used in the "ioa_Volume" and "ioa_Period" fields.
  4204.  
  4205. As always, remember to copy the key value from the request block that reserved 
  4206. the sound channel(s). If your request block's "ioa_AllocKey" value does not 
  4207. match the sound channel's current key value the ADIOERR_NOALLOCATION error 
  4208. message is returned.
  4209.  
  4210. 4.5.1.6  ADCMD_LOCK
  4211.  
  4212. The ADCMD_LOCK command is used to lock one or more channels. If some other 
  4213. program has higher priority and tries to steal our channel(s), the lock is 
  4214. returned to us. The channels are however still not stolen. When the lock is 
  4215. returned we should as fast as possible clean up and free the channels so the 
  4216. other program can get it.
  4217.  
  4218. See the previous sections for more information about this command.
  4219.  
  4220. 4.5.1.7  ADCMD_WAITCYCLE
  4221.  
  4222. If you send a request with the "ADCMD_WAITCYCLE" command set, the request will 
  4223. first be sent back when the sound which is currently being played has reached 
  4224. the end of the cycle. If no sound is being played it will immediately return.
  4225.  
  4226. Remember to copy the key value from the request block that reserved the sound 
  4227. channel(s). If your request block's "ioa_AllocKey" value does not match the 
  4228. sound channel's current key value the ADIOERR_NOALLOCATION error message is 
  4229. returned.
  4230.  
  4231. 4.6  FUNCTIONS
  4232.  
  4233. BeginIO()
  4234.  
  4235. BeginIO() is a low level form of the SendIO() function. The advantage with 
  4236. BeginIO() is that no fields of the request block will be altered as which is 
  4237. the case with SendIO(). When you are using the audio device you should almost 
  4238. always use the BeginIO() and not DoIO() or SendIO(). If you are freeing 
  4239. channels you may however use these functions too.
  4240.  
  4241. Synopsis
  4242. :
  4243.     
  4244. BeginIO( req )
  4245.  
  4246. req:
  4247.     
  4248. (struct IORequest *) Pointer to the request you want to have executed. In this 
  4249. case a pointer to an IOAudio structure.
  4250.  
  4251. DoIO()
  4252.  
  4253. DoIO() is used to send requests to a device, and waits for it to be completed. 
  4254. While the program is waiting it is put to sleep so it will not waste any 
  4255. computer time. DoIO() will return first when the request have been completed or 
  4256. failed, and no message is therefore sent to the reply port. When you are using 
  4257. the audio device you may only use this function when you free audio channel(s). 
  4258.  
  4259.  
  4260.  
  4261. Synopsis
  4262. :
  4263.     
  4264. error = DoIO( req );
  4265.  
  4266. error:
  4267.     
  4268. (long) DoIO() will return first when the request has been completed or 
  4269. something has failed. If the request was successfully completed zero is 
  4270. returned, else an error number is returned. What error number depends on which 
  4271. device was used.
  4272.  
  4273. req:
  4274.     
  4275. (struct IORequest *) Pointer to the request you want to have executed. In this 
  4276. case a pointer to an IOAudio structure.
  4277.  
  4278. SendIO()
  4279.  
  4280. SendIO() is used to send requests to a device, but will return immediately 
  4281. without any delay. To check if the request have been completed use the 
  4282. CheckIO() function, or look at the request's reply port for any messages. Once 
  4283. the request has been completed you must remove the message at the reply port. 
  4284. (CheckIO() will not do it.) To remove a message use the function Remove(). Note 
  4285. that you may NOT close the device before all requests have been completed or 
  4286. aborted!
  4287.  
  4288. When you are using the audio device you may only use this function when you 
  4289. free audio channel(s). 
  4290.  
  4291. Synopsis
  4292. :
  4293.     
  4294. SendIO( req )
  4295.  
  4296. req:
  4297.     
  4298. (struct IORequest *) Pointer to the request you want to have executed. In this 
  4299. case a pointer to an IOAudio structure.
  4300.  
  4301. CheckIO()
  4302.  
  4303. CheckIO() is used to check if a previously started request has been completed. 
  4304. Note that this function will not remove the message at the reply port. This 
  4305. must be done with the Remove() function.
  4306.  
  4307. Synopsis
  4308. :
  4309.     
  4310. ptr = CheckIO( req );
  4311.  
  4312. ptr:
  4313.     
  4314. (long) CheckIO() will either return NULL if the request have not been completed 
  4315. or it will return a pointer to the request block.
  4316.  
  4317. req:
  4318.     
  4319. (struct IORequest *) Pointer to the request you want to check. In this case a 
  4320. pointer to an IOAudio structure.
  4321.  
  4322. WaitIO()
  4323.  
  4324. WaitIO() will wait for the request to be completed, and while the program is 
  4325. waiting it is put to sleep so no computer time is wasted.
  4326.  
  4327. Synopsis
  4328. :
  4329.     
  4330. error = WaitIO( req );
  4331.  
  4332. error:
  4333.     
  4334. (long) WaitIO() will return first when the request, that has previously been 
  4335. sent, has been completed or something has failed. If the request was 
  4336. successfully completed zero is returned, else an error number is returned. What 
  4337. error number depends on which device was used.
  4338.  
  4339. req:
  4340.     
  4341. (struct IORequest *) Pointer to the request you want to wait for to be 
  4342. completed.  In this case a pointer to an IOAudio structure. Note that the 
  4343. request must have already been sent to the device by either a SendIO() or 
  4344. BeginIO() function call.
  4345.  
  4346.  
  4347. AbortIO()
  4348.  
  4349. AbortIO() will try to abort a previously started request. Instead of using this 
  4350. function you can use the ADCMD_FINISH command as explained above.
  4351.  
  4352. A request that is aborted will have its io_Error field set to IOERR_ABORTED 
  4353. (defined in header file "exec/errors.h").
  4354.  
  4355. Synopsis:
  4356.     
  4357. AbortIO( req )
  4358.  
  4359. req:
  4360.     
  4361. (struct IORequest *) Pointer to the request you want to abort. In this case a 
  4362. pointer to an IOAudio structure.
  4363.  
  4364. CloseDevice()
  4365.  
  4366. CloseDevice() will close a device. Note that you should NOT close the device 
  4367. before all started asynchronous requests have either been completed or aborted.
  4368.  
  4369. Synopsis:
  4370.     
  4371. CloseDevice( ioreq );
  4372.  
  4373. ioreg:
  4374.     
  4375. (struct IORequest *) Pointer to the device's request block. In this case a 
  4376. pointer to an IOAudio structure.
  4377.  
  4378. OpenDevice()
  4379.  
  4380. OpenDevice() will try to open the specified device.
  4381.   
  4382. Synopsis:
  4383.     
  4384. error = OpenDevice( name, unit, req, flags );
  4385.  
  4386. error:
  4387.     
  4388. (long) If OpenDevice() managed to open the device it returns 0, else an error 
  4389. number is returned. If you try to reserve one or more audio channels at the 
  4390. same time, and there are not any available, the error message 
  4391. "IOERR_ALLOCFAILED" is returned.
  4392.  
  4393. name:
  4394.     
  4395. (char *) Name of the device you want to open. The name of the audio device is 
  4396. defined as AUDIONAME in header file "audio.device".
  4397.  
  4398. unit:
  4399.     
  4400. (long) Not used by the audio device.
  4401.  
  4402. req:
  4403.     
  4404. (struct IORequest *) Pointer to a request block. In this case a pointer to an 
  4405. IOAudio structure.
  4406.  
  4407. flags:
  4408.     
  4409. (long) Not used by the Audio device.
  4410.  
  4411.  
  4412. 4.7  EXAMPLES
  4413.  
  4414. Example 1
  4415. This program will play some notes (A to G#) with help of the Audio Device. It 
  4416. will use one of the audio channels (the first one which is free).
  4417.  
  4418. Example 2
  4419. This program is very similar to the previous example, but this time are we 
  4420. using double buffered sounds, and there will therefore not be any annoying 
  4421. "clicks" between the notes.
  4422. The technique with double buffered sound is that while the first sound is 
  4423. played the second sound is already sent to the audio device. When the first 
  4424. sound terminates the second sound can immediately start without any delay. 
  4425. While the second sound is being played the first sound is prepared and sent and 
  4426. so on... Because there is never any delay between the sounds there will never 
  4427. be any annoying clicks.
  4428.  
  4429. Example 3
  4430. This program demonstrates how you can play some sampled data. Remember that 
  4431. sampled data is just a more complicated waveform.
  4432.  
  4433. The sampled sound has been converted into numbers by "PrintSound", a utility 
  4434. included in the "Sound" manual.
  4435.  
  4436. Example 4
  4437. This program demonstrates how you can play a sound continuously. While the 
  4438. sound is being played we slowly alter the period and volume values.
  4439.  
  4440. Example 5
  4441. This example demonstrates how you can play sounds in STEREO. First we play a 
  4442. sound in the left channel, then we switch to the right, and then back again, 
  4443. and so on...
  4444.  
  4445. In this example are we reserving the audio channels at the same time as we open 
  4446. the audio device. We use two audio requests, one for the left channel and the 
  4447. other one for the right channel.
  4448.  
  4449. Example 6
  4450. This program will play some notes (A to G#) with help of the Audio Device. It 
  4451. will use as many audio channels as possible, and we are modifying the hardware 
  4452. registers directly instead of using the special Audio Device commands.
  4453.  
  4454. You are allowed to use the hardware registers directly if you make sure that no 
  4455. other task can steel them from you before you have cleared all necessary 
  4456. registers.
  4457.  
  4458.  
  4459. NARRATOR DEVICE
  4460.  
  4461. 5.1  INTRODUCTION
  4462.  
  4463. The Amiga can not only produce crisp clear four channel stereo sound, it can 
  4464. even speak. The operating system was designed so this unique feature easily and 
  4465. efficiently can be used. With very little effort you can let the Amiga read 
  4466. information.
  4467.  
  4468. Sadly very few programs use this extremely useful feature. Although artificial 
  4469. speech sounds a bit monotone and uninteresting it can be used in many 
  4470. situations to transfer information from the computer to the user.
  4471.  
  4472. Imagine if the user has to check hundreds of different numbers on the screen 
  4473. when the original numbers are on papers. This very boring and slow task can be 
  4474. made much easier if the Amiga automatically reads the values and the user only 
  4475. has to look at the paper.
  4476.  
  4477. 5.2  ARTIFICIAL SPEECH
  4478.  
  4479. Imitating human speech is a very difficult process. Even the most sophisticated 
  4480. super computers can still not manage to produce all the different intonations 
  4481. and sounds as a real human voice can. The Amiga's synthesized speech mechanism 
  4482. is not at all as powerful as what these super computers have, but compared to 
  4483. other home computers, it is outstanding.
  4484.  
  4485. Producing artificial speech on the Amiga can be divided into two steps:
  4486.  
  4487. 1. First we have to convert the text we want to be read into phonetical text. 
  4488. Phonetical text is written as the words should sound and not as how the words 
  4489. are spelled.
  4490.      
  4491. If you open a dictionary you will notice that there exist a specification on 
  4492. how every word should be pronounced. The special symbols used to tell you how 
  4493. the word should sound are usually called "the phonetic symbols", and are 
  4494. defined in the beginning or end of the dictionary. The pronunciation of all 
  4495. words can be constructed with help of these symbols.
  4496.      
  4497. On the Amiga we use the same technique. However, since the real phonetical 
  4498. symbols are very strange and can not be typed with a normal keyboard, we use a 
  4499. special "computerised" version of these symbols See next section for a complete 
  4500. list of these phonetical symbols.
  4501.      
  4502. A great news is that you do not have to write the text with the phonetical 
  4503. language. You can instead use a special function called Translate() which can 
  4504. be found in a the "Translator Library". This function can automatically convert 
  4505. english text into phonetics.
  4506.  
  4507. 2. After you have created a string with the phonetical text you send it to the 
  4508. "Narrator Device" which will transform the phonetical symbols into sound, and 
  4509. the text is read.
  4510.  
  4511. 5.2.1  PHONETIC SYMBOLS
  4512.  
  4513. The phonetical symbols used by the dictionaries can sadly not be used on normal 
  4514. computers since they use very strange signs that can not be reproduced on 
  4515. normal keyboards. Instead we have to use a special computerized set phonetical 
  4516. symbols which is defined like this:
  4517.  
  4518.  
  4519.   Sound  Phoeme  Example  Type
  4520.   ---------------------------------
  4521.   A      AE      Hat      Vowel
  4522.          AO      Talk     Vowel
  4523.          EY      Page     Diphthong   (A diphtong is a union
  4524.   ---------------------------------   of two vowel sounds or
  4525.   B      B       Bad      Consonant   vowel letters.)
  4526.   ---------------------------------
  4527.   C      CH      Chin     Consonant
  4528.          K       Car      Consonant
  4529.   ---------------------------------
  4530.   D      D       Did      Consonant
  4531.   ---------------------------------
  4532.   E      EH      Ten      Vowel
  4533.          IY      Feet     Vowel
  4534.   ---------------------------------  
  4535.   F      F       Four     Consonant
  4536.   ---------------------------------  
  4537.   G      G       Got      Consonant
  4538.   ---------------------------------  
  4539.   H      /H      How      Consonant
  4540.   ---------------------------------  
  4541.   I      AY      Five     Diphthong
  4542.          ER      Fur      Vowel
  4543.          IH      Sit      Vowel
  4544.   ---------------------------------  
  4545.   J      J       Yes      Consonant
  4546.   ---------------------------------  
  4547.   K      K       Cat      Consonant
  4548.   ---------------------------------  
  4549.   L      L       Leg      Consonant
  4550.   ---------------------------------  
  4551.   M      M       Man      Consonant
  4552.   ---------------------------------  
  4553.   N      N       No       Consonant
  4554.          NX      Sing     Consonant
  4555.   ---------------------------------  
  4556.   O      AA      Got      Vowel
  4557.          AW      Now      Diphthong
  4558.          AX      About    Vowel
  4559.          IX      Solid    Vowel
  4560.          OH      Saw      Vowel
  4561.          OW      Home     Diphthong
  4562.          OY      Join     Diphthong
  4563.          UH      Put      Vowel
  4564.   ---------------------------------  
  4565.   P      P       Pen      Consonant
  4566.   ---------------------------------  
  4567.   Q      K       Cat      Consonant
  4568.   ---------------------------------  
  4569.   R      R       Red      Consonant
  4570.   ---------------------------------  
  4571.   S      S       Sit      Consonant
  4572.          SH      She      Consonant
  4573.   T      DH      Then     Consonant
  4574.          T       Tea      Consonant
  4575.          TH      Thin     Consonant
  4576.   ---------------------------------  
  4577.   U      AH      Cup      Vowel
  4578.          UW      Too      Diphthong
  4579.   ---------------------------------  
  4580.   V      V       Voice    Consonant
  4581.   ---------------------------------  
  4582.   W      W       Wet      Consonant
  4583.   ---------------------------------  
  4584.   X      /C      Loch     Consonant
  4585.   ---------------------------------  
  4586.   Y      Y       Yellow   Consonant
  4587.   ---------------------------------  
  4588.   Z      Z       Zoo      Consonant
  4589.          ZH      Vision   Consonant
  4590.   ---------------------------------  
  4591.  
  4592. For example, the word "hello" can phonetically be written as "/HEHLOW", and the 
  4593. word "house" as "/HAWZ".
  4594.  
  4595. 5.2.2  INTONATION
  4596.  
  4597. When you are speeking you do not use the same voice all the time. Some parts of 
  4598. the words or sentences are stressed by using a higher voice, were other parts 
  4599. are prounounced more softly.
  4600.  
  4601. For example, the letter "e" in the word "hello" is usually prounounced in a 
  4602. higher voice. By stressing some words the text will sound more interesting, and 
  4603. if used with care it can be a very effective way to get the user's attention.
  4604.  
  4605. Futhermore, by stressing different parts of a sentence the whole meaning of the 
  4606. sentence can change quite dramatically. Take the sentence "I will go home" for 
  4607. example. By stressing different words the user will interpretate the meaning 
  4608. differently. With these four words we can actually get five rather different 
  4609. meanings:
  4610.  
  4611. 1. If no stressin is used, the meaning is simply that the person is going home.
  4612.   
  4613. 2. By stressing the first word "I" it now sounds like the person him/her self 
  4614. is going home but the other persons (if there are any) may do something 
  4615. differently.
  4616.   
  4617. 3. By stressing the second word "will" it now sounds like the persons is really 
  4618. determined and that he/she really wants to go home.
  4619.      
  4620. 4. If the person stresses the third word "go" he/she means that he/she will 
  4621. walk home, although the word "go" can mean travelling by car, train etc.
  4622.  
  4623. 5. Finally, if the last word is stressed the person wants to go to his/her 
  4624. home, and not any other place.
  4625.  
  4626. To stress parts of a word you can put a single digit directly after the phonem 
  4627. which should use a higher intonation. The digit 0 means no special intonation, 
  4628. while the higher digit the more stressed will the phonem be. The digit 9 should 
  4629. be used when you want maximum intonation.
  4630. For example, the stress the letter "e" in the word "hello" the phonetically 
  4631. string should be written as "/HEH4LOW". The phonem "EH" will then be medium 
  4632. stressed.
  4633.  
  4634. 5.2.3  PUNCTATION AND SPECIAL SYMBOLS
  4635.  
  4636. There exist some symbols which are used to help the reader to use the righ 
  4637. intonations. The most commonly used symbole is the puncation ".", but others 
  4638. are the question mark "?", the comma ",", dash "-", etc... Most of these common 
  4639. punctations are understod by the Amiga, and will automatically alter the 
  4640. intonation and speed correctly.
  4641.  
  4642. Here are the symbols you may use:
  4643.  
  4644. Type
  4645.     
  4646. Symbol
  4647.     
  4648. How it affects the intonation
  4649.  
  4650. Punctation
  4651.     
  4652. .
  4653.     
  4654. This will make the intonation of the last word a bit softer, and there will be 
  4655. a long pause before any following words are read.
  4656.  
  4657. Question mark
  4658.     
  4659. ?
  4660.     
  4661. This will cause the Amiga to increase the intonation of the last word so it 
  4662. sounds like a question, and it will be followed by long pause before any 
  4663. following words are read.
  4664.   
  4665. Comma
  4666.     
  4667. ,
  4668.     
  4669. The comma is used to divide a sentense into several parts, but putting a small 
  4670. pause before the following text.
  4671.   
  4672.   Dash
  4673.     
  4674. -
  4675.     
  4676. The dash can be used as the comma, and works much in the same way. There exist 
  4677. a lot of confusing rules on when the dash and when the comma should be used, 
  4678. but I do not intead to expalin it here. (Mainly because I do not know anything 
  4679. about these rules, sadly. I use what looks best, which I think is an excellent 
  4680. rule.) 
  4681.  
  4682.   Parentheses
  4683.     
  4684. ( )
  4685.     
  4686. Parantheses are used to explain something which is not so important that it has 
  4687. to be in the main sentense, but is can still be important for the reader. The 
  4688. text inside parantheses starts off by using less intonation than the rest of 
  4689. the sentense.
  4690.  
  4691. 5.2.4  VOLUME
  4692.  
  4693. Although the intonation is altering the volumne a little, most of the text will 
  4694. be read with the same volume. You can however tell the narrator device to use a 
  4695. completely different volume, and this can be very effective. If you normally 
  4696. use a soft voice, but suddenly increase it to the maximum the user will not 
  4697. miss that part.
  4698.  
  4699. Producing the correct sound can take some time, but once you have mastered it, 
  4700. the text will sound rather smooth and, although a bit computerized, rather 
  4701. human. 
  4702.  
  4703. 5.3  CONVERT TEXT INTO PHONETIC SYMBOLS
  4704.  
  4705. Converting text into phonetical symbols does not have to be a hard task. If you 
  4706. are going to read an English text (or American, Irish, Scottish etc...) you can 
  4707. use the Translate() function. It will automatically convert the text into 
  4708. (almost) correct phonetics which can be used.
  4709.  
  4710. The Translate() function is located in the "Translator" library which must be 
  4711. opened before you can use the function. The Translate() function is actually 
  4712. the only function in this library.
  4713. 5.3.1  OPEN THE TRANSLATOR LIBRARY
  4714.  
  4715. To open the translator library you declare a global pointer to it which must be 
  4716. called "TranslatorBase", and you then call the function OpenLibrary() to open 
  4717. it.
  4718.  
  4719. Synopsis
  4720. :
  4721.     
  4722. library_ptr = OpenLibrary( name, revision );
  4723.  
  4724. library_ptr:
  4725.     
  4726. (struct Library *) If the function could open the library it returns a pointer 
  4727. to it, else NULL is returned. The translator library is located on the system 
  4728. disk, and is therefore loaded when needed. If some other program already has 
  4729. opened it has already been loaded into memory and a pointer is immediately 
  4730. returned to it.
  4731.                
  4732.     
  4733. Be careful to close the library before your program terminates. If you forget 
  4734. it, the library will remain in the memory until the computer is switched off.
  4735.   
  4736. name:
  4737.     
  4738. (char *) Pointer to a string containing the name of the library you want to 
  4739. open. When you open the translator library, the name should be 
  4740. "translator.library".
  4741.   
  4742. revision:
  4743.     
  4744. (long) This value tells the Amiga which oldest version may be used. If there 
  4745. exist a library with the same or higher value the function will successfully 
  4746. open the library, else NULL is returned. If you simply want any version, set 
  4747. this field to zero.
  4748.  
  4749. Here is an example: (Remember that the pointer to the translator library must 
  4750. ALWAYS be called "TranslatorBase"!)
  4751.  
  4752.   /* Pointer to the translator library: */
  4753.   struct Library *TranslatorBase;
  4754.  
  4755.   /* Open the library: */
  4756.   TranslatorBase = OpenLibrary( "translator.library", 0 );
  4757.  
  4758.   /* Have we successfully opened it? */
  4759.   if( !TranslatorBase )
  4760.     clean_up( "Could not open the translator library!" );
  4761.  
  4762. 5.3.2  TRANSLATE TEXT
  4763.  
  4764. Once you have opened the translator device you may start to use the Translate() 
  4765. function. This function will work best with English sentences, but can be used 
  4766. for many other languages if you afterwards do some small changes of the 
  4767. phonetic text.
  4768.  
  4769. Synopsis
  4770. :
  4771.     
  4772. char_left = Translate( in, in_len, out, out_len );
  4773.  
  4774. char_left:
  4775.     
  4776. (long) If not all phonetic text could fit in the out string the function will 
  4777. automatically only translate the words which will fit. (It will not stop in the 
  4778. middle of a word.) If all words could be translated zero is returned, else a 
  4779. negative value is returned. This value tells us how many letters of the "in" 
  4780. string have been translated. With this value we can then call the function 
  4781. again and translate the remaining text.
  4782.              
  4783.     
  4784. Note that the number is negative.
  4785.              
  4786. in:
  4787.     
  4788. (char *) Pointer to the (English) text string which should be read.
  4789.  
  4790. in_len:
  4791.     
  4792. (long) The length of the (English) text string.
  4793.  
  4794. out:
  4795.     
  4796. (char *) Pointer to a string where the phonetic text string will be stored. 
  4797. (The "in" string is converted and copied to the "out" string.)
  4798.              
  4799. out_len:
  4800.     
  4801. (long) The length of the phonetic (out) string.
  4802.  
  4803. Here is an example on how to translate a string. The nice thing is that we can 
  4804. translate strings of any size. If the translated string does not fit in the 
  4805. buffer, we divide it up into smaller parts.
  4806.  
  4807.   /* Translated buffer size: */
  4808.   #define PHONETIC_BUFFER_SIZE 50
  4809.  
  4810.   /* Number of characters that were translated, or */
  4811.   /* zero if all characters were translated:       */
  4812.   int char_translated;
  4813.   
  4814.   /* This variable contains the current position */
  4815.   /* in the string which is translated:          */
  4816.   int current_position;
  4817.    
  4818.   /* The original string: */
  4819.   char *original_string = "The Amiga C Encyclopedia!";
  4820.   
  4821.   /* The phonetic string: */
  4822.   char phonetic_string[ PHONETIC_BUFFER_SIZE ];
  4823.  
  4824.  
  4825.   /* Open the translator library: */
  4826.   TranslatorBase = (struct Library *)
  4827.     OpenLibrary( "translator.library", 0 );
  4828.  
  4829.   /* Have we successfully opened the library? */
  4830.   if( !TranslatorBase )
  4831.     clean_up( "Could not open the translator library!" );
  4832.  
  4833.  
  4834.   /* Start with the first character in the original string: */
  4835.   current_position = 0;
  4836.   
  4837.   /* Translate (parts of) our string into phonetics: */
  4838.   char_translated =
  4839.     Translate( original_string,
  4840.                strlen( original_string ),
  4841.                phonetic_string,
  4842.                PHONETIC_BUFFER_SIZE );
  4843.  
  4844.   /* Print the translated part: */
  4845.   printf( "%s\n", phonetic_string );
  4846.  
  4847.  
  4848.   /* As long as Translate() does not return zero we stay */
  4849.   /* in this while loop and continues to translate the   */
  4850.   /* original string into phonetics:                     */
  4851.   while( char_translated )
  4852.   {
  4853.     /* Increase the current position in the original string:  */
  4854.     /* (Remember that "char_translated" variable is negative, */
  4855.     /* and we must therefore use the "-=" operator and not    */
  4856.     /* the "+=" to increase the current position.[-- = +])    */
  4857.     current_position -= char_translated;
  4858.  
  4859.     /* Translate the following part our string into phonetics: */
  4860.     /* (Note that when we put brackets after a string we we    */
  4861.     /* get the character at the specified position, but since  */
  4862.     /* we want the address of that position we also have to    */
  4863.     /* put the pointer "&" sign in front of the string.)       */
  4864.     char_translated =
  4865.       Translate( &original_string[ current_position],
  4866.                  strlen( &original_string[ current_position] ),
  4867.                  phonetic_string,
  4868.                  PHONETIC_BUFFER_SIZE );
  4869.  
  4870.     /* Print the translated part: */
  4871.     printf( "%s\n", phonetic_string );
  4872.   }
  4873.  
  4874.   /* All words have now been translated! */
  4875.  
  4876. 5.3.3  CLOSE THE TRANSLATOR LIBRARY
  4877.  
  4878. Before your program terminates it must close the translator library. This is 
  4879. especially important with this library since it is loaded from the system disk 
  4880. when opened, and if you do not close it a lot of memory is wasted. Be careful 
  4881. about this.
  4882.  
  4883. As I have said many times, make sure that your program does not only terminate 
  4884. nicely when it has reached the end. It must also manage to terminate nicely if 
  4885. it suddenly has to quit because of
  4886. some error. If you look at the examples which accompanies this manual you will 
  4887. see that most of them use a function called "clean_up()". The idea with this 
  4888. function is that it examines all resources, and if it finds out that something 
  4889. has been opened or allocated it closes it before the program terminates. 
  4890. Because it examines all the resources before it attempts to close or deallocate 
  4891. them the function can always be called and it will clean up everything, even if 
  4892. you only have opened and allocated some resources. Try to use something similar 
  4893. in your own programs.
  4894.  
  4895. 5.4  READ PHONETIC SYMBOLS
  4896.  
  4897. The translator library and the function Translate() are only used to convert 
  4898. normal (English) text into phonetical strings. These phonetical strings can 
  4899. then be read by the Amiga, but to do this you have to use another resource on 
  4900. the Amiga which is the famous "Narrator Device". (Tataaa, trumpets and drums!) 
  4901.  
  4902. The narrator device is very straight forward and easy to use. As always you use 
  4903. request blocks to send your commands, and the device will reply by sending 
  4904. messages to a reply (message) port. The device should of course first be opened 
  4905. by calling the OpenDevice() function. When your program wants to terminate the 
  4906. device and message port are closed and the request block is deallocated.
  4907.  
  4908.  
  4909.  
  4910. 5.4.1  NARRATOR REQUEST BLOCK
  4911.  
  4912. When you are using the narrator device you have to use an extended request 
  4913. block called "narrator_rb" which is defined like this: (Defined in header file 
  4914. "devices/narrator.h".)
  4915.  
  4916. struct narrator_rb
  4917. {
  4918.   struct IOStdReq message;
  4919.   UWORD  rate;
  4920.   UWORD  pitch;
  4921.   UWORD  mode;
  4922.   UWORD  sex;
  4923.   UBYTE  *ch_masks;
  4924.   UWORD  nm_masks;
  4925.   UWORD  volume;
  4926.   UWORD  sampfreq;
  4927.   UBYTE  mouths; 
  4928.   UBYTE  chanmask;
  4929.   UBYTE  numchan;
  4930.   UBYTE  pad;
  4931. };
  4932.  
  4933. message:
  4934.     
  4935. The top part of this request block consists of the standard input output 
  4936. request block. This IOStdReq block is explained below.
  4937.  
  4938. rate:
  4939.     
  4940. The speaking rate in words per minute. The default rate (DEFRATE) has been 
  4941. defined as 150 words per minute. The maximum speed (MAXRATE) is 400 words and 
  4942. the minimum speed (MINRATE) is 40 words per minute.
  4943.  
  4944. pitch:
  4945.     
  4946. The voice's pitch. This is the medium pitch value. Stressed words are spoken 
  4947. with a higher pitch, while soft words are spoken with a lower pitch. By 
  4948. changing this value the whole voice is affected.
  4949.  
  4950.     
  4951. The default pitch (DEFPITCH) is 110. The maximum pitch (MAXPITCH) is 320 and 
  4952. the minimum pitch is 65.
  4953.  
  4954. mode:
  4955.     
  4956. This value will affect the way the text is read. If you set the flag 
  4957. "NATURALF0" the voice will go up and down as the text is read. On the other 
  4958. hand, if you set the flag "ROBOTICF0" the voice will be very dull and does not 
  4959. change as the text is read.
  4960.  
  4961. sex:
  4962.     
  4963. If the flag "MALE" is set a dark male voice will be used. If you instead set 
  4964. the flag "FEMALE" a lighter and sharper voice will be used. (Do not ask me why 
  4965. they did not define it as "BITCH".)
  4966.  
  4967. *ch_masks:
  4968.     
  4969. This field should be given a pointer to an "allocation array" which specifies 
  4970. which audio channels that should be used.
  4971.            
  4972.     
  4973. There exist four sound channels which can be used. The first and last sound 
  4974. channel is connected to the left audio port, while the second and third is 
  4975. connected to the right audio port.
  4976.  
  4977.     
  4978. To specify which channels you want to use you use a value where the first bit 
  4979. represents the first sound channel, the second bit the second sound channel and 
  4980. so on...
  4981.     
  4982. Bit:         3     2      1      0
  4983.     
  4984. -------------------------------------
  4985.     
  4986. Value:       8     4      2      1
  4987.     
  4988. Channel:     3     2      1      0
  4989.     
  4990. Audio port:  Left  Right  Right  Left
  4991.  
  4992.     
  4993. To reserve the first and last channel (both using the left sound channel) you 
  4994. would use the value 9 (1001[bin] = 9[dec]). To reserve the second and third 
  4995. channel (both using the right sound channel) you would use the value 6 
  4996. (0110[bin] = 6[dec]). To reserve all four sound channels set the value to 15 
  4997. (1111[bin] = 15[dec]).
  4998.            
  4999.     
  5000. The allocation array should consist of all the channel combinations you like. 
  5001. The narrator device will first try to reserve the channels specified by the 
  5002. first value. If it could not get these channels, it tries the next values until 
  5003. if finds a combination it could reserve or it reaches the end of the allocation 
  5004. array and the operation fails.
  5005.            
  5006.     
  5007. For example. If you want that the text should be read in stereo you have to use 
  5008. one audio channel from the right and one from the left port. There exist four 
  5009. possible combinations which satisfy this, and the "allocation array" should 
  5010. therefore look like this:
  5011.            
  5012.     
  5013. UBYTE allocation_array = { 3, 5, 10,12 };
  5014.  
  5015.     
  5016. Dec    Bin     Description
  5017.     
  5018. ---------------------------------------------------
  5019.     
  5020. 3    0011    First left and first right channel.
  5021.     
  5022. 5    0101    First left and second right channel.
  5023.     
  5024. 10   1010    Second left and first right channel.
  5025.     
  5026. 12   1100    Second left and second right channel.
  5027.  
  5028. nm_masks:
  5029.     
  5030. This field should be given a number which tells the narrator device how many 
  5031. values there are in the allocation array. 
  5032.  
  5033. volume:
  5034.     
  5035. The volume of the voice. The default volume (DEFVOL) is the same as the maximum 
  5036. volume (MAXVOL) which is defined as 64. The minimum volume (MINVOL) is 0 which 
  5037. is the same thing as silent.
  5038.            
  5039.     
  5040. I myself does not like that the maximum volume is often used. It would be much 
  5041. better if everyone were using the medium volume as the default value. The user 
  5042. will then turn up the volume on his/her stereo to compensate for the lower 
  5043. volume. The advantage is that if you now suddenly play a very loud sound or use 
  5044. a very loud voice it will also be very loud.
  5045.            
  5046.     
  5047. Imagine a game which is normally using a sound level of 32. The user is sitting 
  5048. alone in his/her own room, and it is very late. Imagine what would happen if a 
  5049. monster would appear in the the game and at the same time the volume was 
  5050. increased to maximum. It will undoubtedly be a nice (hmmm) surprise for the 
  5051. user.
  5052.  
  5053. sampfreq:
  5054.     
  5055. This field specifies which frequency should be used. The default frequency 
  5056. (DEFFREQ) is 22200. The maximum value (MAXFREQ) is 28000 and the minimum value 
  5057. (MINFREQ) is 5000. 
  5058.  
  5059. mouths:
  5060.     
  5061. Normally this field should be set to zero. However, of you are going to use the 
  5062. "mouth" request, as explained below, you should set a non zero value.
  5063. chanmask:
  5064.     
  5065. This field contains the value which was used of the allocation array to reserve 
  5066. the audio channel(s). This field is used by the system, but if you like you may 
  5067. examine it, but you may never change it.
  5068.  
  5069. numchan:
  5070.     
  5071. Number of channels used. This field is also used by the system, but if you like 
  5072. you may examine it, but you may never change it.
  5073.  
  5074. pad:
  5075.     
  5076. This field is not used, and should therefore never be used.
  5077.  
  5078. The IOStdReq structure (which is a part of the narrator_rb structure) is 
  5079. defined in the "exec/io.h" header file like this: (Only some parts of this 
  5080. structure will be used, so you do not have bother too much about it.)
  5081.  
  5082. struct IOStdReq
  5083. {
  5084.   struct Message io_Message;
  5085.   struct Device  *io_Device;
  5086.   struct Unit    *io_Unit;
  5087.   UWORD  io_Command;
  5088.   UBYTE  io_Flags;
  5089.   BYTE   io_Error;
  5090.   ULONG  io_Actual;
  5091.   ULONG  io_Length;
  5092.   APTR   io_Data;
  5093.   ULONG  io_Offset;
  5094. };
  5095.  
  5096. io_Message:
  5097.     
  5098. The top part consists of a Message structure which will be sent to us when the 
  5099. request has been completed (successfully or not). This message structure will 
  5100. automatically be given a pointer to the reply port when the narrator_rb 
  5101. structure is created, and the priority will automatically be set when used, so 
  5102. we do not have to bother too much about it.
  5103.  
  5104. io_Device:
  5105.     
  5106. This field will automatically be initialized when you use this request block 
  5107. together with an OpenDevice() function call. It is simply a pointer to the 
  5108. device which this request block is made for.
  5109.  
  5110. io_Unit:
  5111.     
  5112. This field will automatically be initialized when you send the request block to 
  5113. the narrator device.
  5114.  
  5115. io_Command:
  5116.     
  5117. The following commands are accepted by the narrator device, and may be used: 
  5118. (Will be explained below.)
  5119.  
  5120.     
  5121. CMD_WRITE
  5122.     
  5123. Start to speak.
  5124.     
  5125. CMD_STOP
  5126.     
  5127. Stop all speech requests.
  5128.     
  5129. CMD_START
  5130.     
  5131. Start to speak again.
  5132.     
  5133. CMD_FLUSH
  5134.     
  5135. Remove all queued requests.
  5136.     
  5137. CMD_RESET
  5138.     
  5139. Reset the narrator device.
  5140.  
  5141. io_Flags:
  5142.     
  5143. No flags are used.
  5144.  
  5145. io_Error:
  5146.     
  5147. If the request can not successfully be executed by the narrator device, it will 
  5148. be returned with one of the following error flags:
  5149.  
  5150.  
  5151.     
  5152. ND_NoMem
  5153.     
  5154. Not enough memory for the request.
  5155.     
  5156. ND_NoAudLib
  5157.     
  5158. Can not open the audio device.
  5159.     
  5160. ND_MakeBad
  5161.     
  5162. Can not execute the MakeLibrary() function.
  5163.     
  5164. ND_UnitErr
  5165.     
  5166. Wrong unit number (not 0).
  5167.     
  5168. ND_CantAlloc
  5169.     
  5170. Can not find any free audio channels to reserve.
  5171.     
  5172. ND_Unimpl
  5173.     
  5174. Not a valid command.
  5175.     
  5176. ND_NoWrite
  5177.     
  5178. Nothing is being read, so you can not read any "mouth" values.
  5179.     
  5180. ND_Expunged
  5181.     
  5182. Wrong expunge value.
  5183.     
  5184. ND_PhonErr
  5185.     
  5186. Wrong phonetic code.
  5187.     
  5188. ND_RateErr
  5189.     
  5190. Too large or small rate value
  5191.     
  5192. ND_PitchErr
  5193.     
  5194. Too large or small pitch value
  5195.     
  5196. ND_SexErr
  5197.     
  5198. Wrong sex number used.
  5199.     
  5200. ND_ModeErr
  5201.     
  5202. Wrong mode value used.
  5203.     
  5204. ND_FreqErr
  5205.     
  5206. Too high or low frequency value.
  5207.     
  5208. ND_VolErr
  5209.     
  5210. Too high or low volume value.
  5211.  
  5212. io_Actual:
  5213.     
  5214. Used by the system.
  5215.  
  5216. io_Length:
  5217.     
  5218. The number of characters in the phonetic string that should be read.
  5219.  
  5220. io_Data:
  5221.     
  5222. Pointer to the phonetic sting.
  5223.  
  5224. io_Offset:
  5225.     
  5226. Used by the system.
  5227.  
  5228. 5.4.2  OPEN THE NARRATOR DEVICE
  5229.  
  5230. To open the narrator device does not differ from opening other devices. First 
  5231. you have to create a message port to which the device can send messages to you. 
  5232. Secondly you have to create one or more narrator_rb structures by using the 
  5233. CreateExtIO() function. Finally you can open the narrator device with help of 
  5234. one of the request blocks.
  5235.  
  5236. 1. Open a message port. Since it is only our task and the narrator device that 
  5237. will use the message port, we do not need to make it "public", therefore no 
  5238. name. Priority should as usual be set to 0, normal priority.
  5239.  
  5240.        struct MsgPort *replymp;
  5241.  
  5242.        replymp = (struct MsgPort *)
  5243.          CreatePort( NULL, 0 );
  5244.        if( !replymp )
  5245.          clean_up( "Could not create the reply port!" );
  5246.  
  5247. 2. Allocate a request block of type narrator_rb structure. The narrator_rb 
  5248. structure is an extended version of the normal request block, and should 
  5249. therefore be allocated with help of the CreateExtIO() function with the size 
  5250. set to sizeof( struct narrator_rb ).
  5251.  
  5252.        struct narrator_rb *narrator_req;
  5253.  
  5254.        narrator_req = (struct narrator_rb *)
  5255.          CreateExtIO( replymp, sizeof( struct narrator_rb ) );
  5256.  
  5257.        if( !narrator_req )
  5258.          clean_up( "Not enough memory!" );
  5259. 3. Once the message port and the request block have successfully been created 
  5260. you can "open" (gain access to) the narrator device. The default values will 
  5261. now also be set in the request block.
  5262.  
  5263.        error = OpenDevice( "narrator.device", 0, narrator_req, 0 );
  5264.  
  5265.        if( error )
  5266.          clean_up( "Could not open the Narrator Device!" );
  5267.  
  5268. 5.4.3  READ PHONETIC TEXT 
  5269.  
  5270. Once you have successfully opened the narrator device you may start to use the 
  5271. narrator device. The fields of the request block with which the narrator device 
  5272. was opened with have automatically been given the default values:
  5273.  
  5274. 1. The rate is set to 150 words per minute.
  5275. 2. The pitch is set to 110 Hz.
  5276. 3. The mode is set to normal (living) voice.
  5277. 4. The sex field is set to man.
  5278. 5. The sampfreq is set to 22200.
  5279. 6. The volume is set to 64.
  5280.  
  5281. The following fields must be initialized by yourself:
  5282.  
  5283. 1. You must give the "io_Data" field of the IOStdReq structure a pointer to the 
  5284. string of phonetic words that should be read.
  5285.   
  5286. 2. Give the "io_Length" field of the IOStdReq structure the number of 
  5287. translated characters that should be read. You can use the function strlen() to 
  5288. find out how long the translated string is.
  5289.  
  5290. 3. You have to give the "ch_masks" field a pointer to the allocation array that 
  5291. should be used.
  5292.  
  5293. 4. Give the "nm_masks" field the number of values which exist in the allocation 
  5294. array.
  5295.  
  5296. 5. Finally you should give the "io_Command" field of the IOStdReq structure the 
  5297. "CMD_WRITE" flag. (We are going to send [write] text to the narrator device.)
  5298.  
  5299. Here is an example:
  5300.  
  5301.   /* The text should be read in stereo: */
  5302.   UBYTE allocation_array[]=
  5303.   {
  5304.     LEFT0F|RIGHT0F, /* First left and first right channel.   */
  5305.     LEFT0F|RIGHT1F, /* First left and second right channel.  */
  5306.     LEFT1F|RIGHT0F, /* Second left and first right channel.  */
  5307.     LEFT1F|RIGHT1F  /* Second left and second right channel. */
  5308.   };
  5309.  
  5310.   /* The phonetic string: */  
  5311.   char *phonetic_string = "/HEH4LOW";
  5312.  
  5313.  
  5314.   /* Message port opened...    */
  5315.   /* "narrator_rb" created...  */
  5316.   /* Narrator device opened... */
  5317.  
  5318.  
  5319.   /* Set our requirements: */
  5320.  
  5321.   /* 1. Give it a pointer to the phonetic string: */
  5322.   narrator_req->message.io_Data = (APTR) phonetic_string;
  5323.  
  5324.   /* 2. Set the length of the phonetic string: */
  5325.   narrator_req->message.io_Length = strlen( phonetic_string );
  5326.  
  5327.   /* 3. Desired channel combinations: */
  5328.   narrator_req->ch_masks = allocation_array;
  5329.  
  5330.   /* 4. Size of the allocation array: */
  5331.   narrator_req->nm_masks = sizeof( allocation_array );
  5332.  
  5333.   /* 5. Send (write) the text to the device: */
  5334.   narrator_req->message.io_Command = CMD_WRITE;
  5335.  
  5336. You may of course also change the other values if you do not want to use the 
  5337. default ones.
  5338.  
  5339. After the request has been initialized can it be sent to the narrator device 
  5340. which will read the phonetic string. To send request you can either use the 
  5341. synchronous command DoIO() or the asynchronous command SendIO().
  5342.  
  5343. The DoIO() function will send the request to the device and put your program to 
  5344. sleep. When the device has finished your request it is returned and your 
  5345. program wakes up. If something failed will DoIO() return an error number and a 
  5346. copy of their value will also be stored in the "io_Error" field of the request 
  5347. block.
  5348.  
  5349. The SendIO() function should be used when you want to continue to do something 
  5350. while the narrator device is reading the text. After you have called SendIO() 
  5351. you can put your task to sleep by either using the WaitIO() or Wait() function. 
  5352. If you want to check if the request has been completed you can use the 
  5353. CheckIO() function.
  5354.  
  5355. All these functions have been described in chapter "Devices", and will 
  5356. therefore not be repeated here.
  5357.  
  5358. An example on a synchronous call: (Your program is put to sleep while the 
  5359. narrator device completes your request.)
  5360.  
  5361.   BYTE error;
  5362.  
  5363.   /* ... */
  5364.  
  5365.   /* Read the text: */
  5366.   error = DoIO( narrator_req );
  5367.  
  5368.   /* Were there any errors? */
  5369.   if( error )
  5370.     clean_up( "Problems with reading the text!" );
  5371.  
  5372. An example on an asynchronous call: (Your program continues to run while the 
  5373. text is read.)
  5374.  
  5375.  
  5376.   /* Start to read: */
  5377.   SendIO( narrator_req );
  5378.  
  5379.   while( !CheckIO( narrator_req ) )
  5380.   {
  5381.     /* Do something... */
  5382.   }
  5383.  
  5384.   /* Collect message at the reply port... */
  5385.  
  5386.   /* Were there any errors? */
  5387.   if( narrator_req->message.io_Error )
  5388.     clean_up( "Problems with reading the text!" );
  5389.  
  5390. 5.4.4  USING SEVERAL REQUEST BLOCKS
  5391.  
  5392. If you have created several request blocks will only the one which you opened 
  5393. the narrator device with be correctly preinitialized. These structures which 
  5394. have not been initialized must be prepared before they may be used. The 
  5395. simplest solution is actually to copy the whole structure, because you will 
  5396. then be sure that no values are forgotten.
  5397.  
  5398. Here is an example:
  5399.  
  5400.   /* Source and destination pointers: */
  5401.   BYTE *first_ptr;
  5402.   BYTE *second_ptr;
  5403.  
  5404.  
  5405.   /* Allocate request blocks, open narrator device, etc...   */
  5406.   /* (The two request blocks are called "first_narrator_req" */
  5407.   /* and "second_narrator_req".)                             */
  5408.  
  5409.   /* Copy the first request block to the second one: */
  5410.  
  5411.   /* Get the start addresses of both request blocks: */
  5412.   first_ptr = (BYTE *) first_narrator_req;
  5413.   second_ptr = (BYTE *) second_narrator_req;
  5414.  
  5415.   /* Copy byte by byte: */
  5416.   for( loop = 0; loop < sizeof( struct narrator_rb ); loop++ )
  5417.   {
  5418.     /* Copy: */
  5419.     *second_ptr = *first_ptr;
  5420.     
  5421.     /* Next byte: */
  5422.     first_ptr++;
  5423.     second_ptr++;
  5424.   }
  5425.  
  5426. 5.4.5  CLEAN UP  
  5427.  
  5428. When your program terminates you have to clean up and free all allocated 
  5429. resources. This is what has to be done:
  5430. 1. Remove any messages still left at the message (reply) port.
  5431. 2. Close the message (reply) port(s).
  5432. 3. Close the narrator device.
  5433. 4. Deallocate the request blocks.
  5434.  
  5435. 5.4.5.1  REMOVE ALL MESSAGES
  5436.  
  5437. Before you close anything you should first remove all messages from the message 
  5438. (reply) port. A short while loop that removes the messages until it can not 
  5439. find any more is a simple but effective solution.
  5440.  
  5441.   while( GetMsg( replymp ) )
  5442.     printf( "Collected a message at the reply port.\n" );
  5443.  
  5444. 5.4.5.2  CLOSE MESSAGE PORT
  5445.  
  5446. After all messages have been removed from the port you may close it by calling 
  5447. the DeletePort() function. Note that you should never try to close a message 
  5448. port you have not opened.
  5449.  
  5450.   DeletePort( replymp);
  5451.  
  5452. 5.4.5.3  CLOSE THE NARRATOR DEVICE
  5453.  
  5454. The narrator device itself must of course also be closed if you have opened it. 
  5455. Note that you should never try to close a device you have not opened.
  5456.  
  5457.   CloseDevice( narrator_req );
  5458.  
  5459. 5.4.4  DEALLOCATE THE REQUEST BLOCKS
  5460.  
  5461. Finally you should deallocate all request blocks you have created. Use the 
  5462. DeleteExtIO() function. The size should be set to the size of a narrator_rb 
  5463. structure. Just remember to close the narrator device before you deallocate the 
  5464. request block(s).
  5465.  
  5466.   DeleteExtIO( narrator_req, sizeof( struct narrator_rb ) );
  5467.  
  5468. 5.5  THE NARRATOR'S MOUTH
  5469.  
  5470. The narrator device offers a rather unique but sometimes useful feature. The 
  5471. narrator device can be used to draw a mouth as the text is read. It may sound a 
  5472. bit strange, but if you are using animations this can be very helpful. 
  5473.  
  5474. The narrator device will tell us how wide and high the mouth should be, and 
  5475. with this we can easily construct a simple mouth. 
  5476.  
  5477. 5.5.1  MOUTH REQUEST BLOCK
  5478.  
  5479. To use this "mouth" feature you have to use a special request block which is 
  5480. called "mouth_rb", and looks like this: (Defined in header file 
  5481. "devices/narrator.h".)
  5482.  
  5483.  
  5484. struct mouth_rb
  5485. {
  5486.   struct narrator_rb voice;
  5487.   UBYTE  width;
  5488.   UBYTE  height;
  5489.   UBYTE  shape;
  5490.   UBYTE  pad;
  5491. };
  5492.  
  5493. voice:
  5494.     
  5495. The top part of the mouth block consists of a "narrator_rb" structure. When you 
  5496. create this structure the message port will automatically be initialized. The 
  5497. following fields must however be initialized by yourself:
  5498.  
  5499.     
  5500. 1. You must copy the "io_Device" pointer from the request which is reading the 
  5501. text.
  5502.     
  5503. 2. The "io_Unit" number must also be copied.
  5504.     
  5505. 3. The "io_Error" field should be set to zero.
  5506.     
  5507. 4. Finally you should set the command "CMD_READ" in the "io_Command" field.
  5508.  
  5509.     
  5510. After you have sent this request block and it is returned you can examine the 
  5511. "io_Error" field to check if there were any problems. See above for a complete 
  5512. list of error flags.
  5513.         
  5514.     
  5515. When you are using this request, it will be returned to you if one of the 
  5516. following thing has happened:
  5517.         
  5518.     
  5519. 1. The size of the mouth has changed.
  5520.     
  5521. 2. Something went wrong, and the "io_Error" field will contain an error flag. 
  5522. If you have received the "ND_NoWrite" the Amiga has stopped talking, and you 
  5523. should not try to draw any mouth any more.
  5524.  
  5525. width:
  5526.     
  5527. After you have successfully executed this request you can examine this field to 
  5528. check how wide the mouth should be. The request block will only be returned 
  5529. when the size of the mouth has changed, or the Amiga has stopped talking. 
  5530.  
  5531. height:
  5532.     
  5533. This field will contain the height of the mouth.
  5534.  
  5535. shape:
  5536.     
  5537. This field may only be used by the system, and should never be changed.
  5538.  
  5539. pad:
  5540.     
  5541. No used, and should not be used.
  5542.  
  5543. 5.5.2  CREATE A MOUTH REQUEST BLOCK
  5544.  
  5545. Since the size of the mouth_rb is larger than the standard sized request block 
  5546. you have to use the CreateExtIO() function to allocate and preinitialize the 
  5547. request block. Normally it is easiest to connect this structure to the same 
  5548. message port as the other request blocks are connected to. You can of course 
  5549. use a separate message port if you want.
  5550.  
  5551.   struct mouth_rb *mouth_req;
  5552.  
  5553.   mouth_req = (struct mouth_rb *)
  5554.     CreateExtIO( replymp, sizeof( struct mouth_rb ) );
  5555.   if( !mouth_req )
  5556.     clean_up( "Not enough memory!" );
  5557. 5.5.3  PREPARE THE MOUTH REQUEST BLOCK
  5558.  
  5559. After you have created the mouth_rb structure you have to copy some values, as 
  5560. explained above, from the request block which will read the text. You must also 
  5561. set some values yourself.
  5562.  
  5563. Here is an example on how to initilaize the mouth request:
  5564.  
  5565.   /* Set the mouth width and height to zero: */
  5566.   mouth_req->width = 0;
  5567.   mouth_req->height = 0;
  5568.  
  5569.   /* Give the mouth request a pointer to the narrator device: */
  5570.   mouth_req->voice.message.io_Device = narrator_req->message.io_Device;
  5571.  
  5572.   /* Give the mouth request the current unit number: */
  5573.   mouth_req->voice.message.io_Unit = narrator_req->message.io_Unit;
  5574.  
  5575.   /* No error number (so far): */
  5576.   mouth_req->voice.message.io_Error = 0;
  5577.  
  5578.   /* The mouth request should look at (read) the request */
  5579.   /* which is currently talking:                         */
  5580.   mouth_req->voice.message.io_Command = CMD_READ;
  5581.  
  5582. The mouth_rb request block can now be used.
  5583.  
  5584. 5.5.4  GET THE SIZE OF THE MOUTH
  5585.  
  5586. The mouth request block should be sent to the narrator device after you have 
  5587. sent a normal read request. If the device is currently not reading any text the 
  5588. mouth request will immediately be returned with the error flag "ND_NoWrite" 
  5589. set.
  5590.  
  5591. If the device is reading some text the mouth request will first be returned 
  5592. when the mouth has changed size, or if the narrator device has reached the end 
  5593. of the text string. If the device reached the end of the string and stops 
  5594. talking the mouth request will be returned with the error flag "ND_NoWrite" 
  5595. set. However, if the request is returned and no error flag is set, the mouth 
  5596. has changed size and should be redrawn.
  5597.  
  5598. Since your program must be able to work while the narrator device is reading 
  5599. the text you have to use the asynchronous command SendIO() to start the read 
  5600. request. Here is an example on how to use the narrator's mouth:
  5601.  
  5602.   /* TRUE as long the Amiga is reading text: */
  5603.   BOOL still_talking;
  5604.  
  5605.   /* ... */
  5606.  
  5607.   /* Start to read: (The read request must be sent by */
  5608.   /* the asynchronous command SendIO().)              */
  5609.   SendIO( narrator_req );
  5610.  
  5611.   /* The Amiga is reading the text: */
  5612.   still_talking = TRUE;
  5613.  
  5614.   /* As long as the Amiga is reading the text */
  5615.   /* we stay in the while loop:               */
  5616.   while( still_talking )
  5617.   {
  5618.     /* Send the mouth request to the narrator device. The request */
  5619.     /* will be returned when the mouth width and/or height have   */
  5620.     /* changed, or the device has stopped reading the text:       */ 
  5621.     DoIO( mouth_req );
  5622.     
  5623.     /* Has the device stopped reading the text: */
  5624.     if( mouth_req->voice.message.io_Error == ND_NoWrite )
  5625.       still_talking = FALSE;
  5626.     else
  5627.     {
  5628.       /* No, the device is still reading. The mouth must have */
  5629.       /* changed, so we better redraw it:                     */
  5630.  
  5631.       /* Draw the new mouth... */ 
  5632.     }
  5633.   }
  5634.  
  5635.   /* We know that the Amiga has stopped speaking,  */
  5636.   /* and hence we do not have to wait for the      */
  5637.   /* narrator to finish reading. We should however */
  5638.   /* check if we have successfully read the text:  */
  5639.   if( narrator_req->message.io_Error )
  5640.     clean_up( "Error while reading!" );
  5641.  
  5642. 5.6  EXAMPLES
  5643.  
  5644. Example 1
  5645. This very simple example demonstrates how to open the translator library, 
  5646. translate a string, and finally close the library before the program 
  5647. terminates.
  5648.  
  5649. Example 2
  5650. This example demonstrates how you can use a while loop to translate parts of a 
  5651. string until the whole string has been translated.
  5652.  
  5653. Example 3
  5654. This example demonstrates how to translate a string into a phonetical string 
  5655. which is then read by the narrator device.
  5656.  
  5657. Example 4
  5658. This example very similar to the previous one, but this time are we using a 
  5659. different voice. By altering the rate, pitch, mode, sex and volume, you can 
  5660. produce very different sounds.
  5661.  
  5662. Example 5
  5663. This example demonstrates how you can let the Amiga read small stories. By 
  5664. altering the rate, pitch, mode, sex and volume parameters it can sound like 
  5665. several persons are talking. It can also be used to express emotions and stress 
  5666. important parts of the text.
  5667.  
  5668. This example is using some home made functions which makes life a little bit 
  5669. easier. If you have to read a lot of text I recommend you to use special 
  5670. functions like these. It will then be much easier to write (and read) the 
  5671. program code.
  5672.  
  5673. Example6
  5674. This example demonstrates how to use the mouth request block to draw a talking 
  5675. mouth.
  5676.  
  5677.  
  5678. TRACKDISK DEVICE
  5679.  
  5680. 6.1  INTRODUCTION
  5681.  
  5682. All Amiga models are delivered with at least one internal disk drive, but you 
  5683. may connect up to three extra disk drives. The disk drivers are using normal 
  5684. double sided double density (2DD) 3 1/2" disks. 5 1/4" disks may also be used, 
  5685. but this is not supported by the trackdisk device. 
  5686.  
  5687. The trackdisk device consists of a set of low level routines which are used by 
  5688. other higher level processes like AmigaDOS. When you want to read and write 
  5689. data you should normally only use AmigaDOS or the special file commands in C. 
  5690. The trackdisk device should only be used when you want to write disk 
  5691. viewers/editors, or disk-copy programs.
  5692.  
  5693. Although the disk routines are normally considered to be very slow on the 
  5694. Amiga, it should be noted that it is actually not the disk drivers which are 
  5695. slow. AmigaDOS is using a very flexible disk operating system and because of 
  5696. this the disk drivers appears to be very slow. The low level routines are on 
  5697. the other hand very quick and are turbocharged with the "Blitter" (special 
  5698. coprocessor on the Amiga) which is specialized in moving large quantities of 
  5699. data extremely fast.
  5700.  
  5701. To read and write so called "raw data" with the lowlevel routines is much 
  5702. faster that to use the higher and more sophisticated AmigaDOS routines. Many 
  5703. games use these low level routines to quickly load graphics and sound effects.
  5704.  
  5705. Normal programs should, as said before, not use the lowlevel routines described 
  5706. here. However, it can still be interesting to read about it anyway, since it is 
  5707. always good to know what is actually happening inside the Amiga. 
  5708.  
  5709. 6.2  AMIGA DISK DRIVERS
  5710.  
  5711. The trackdisk device was designed to handle normal double sided double density 
  5712. (2DD) 3 1/2" disks (also called "1 megabyte disks"). The disk can be logically 
  5713. (fysically too, but that is not recommended) be divided into several small data 
  5714. areas where the actual data is stored.
  5715.  
  5716. Each data area, normally called "Sector", can store 512 bytes of data and 16 
  5717. bytes of so called "label" data. The label area is used to identify the sector 
  5718. and what is stored here, and can therefore not be used to store raw data.
  5719.  
  5720. A group of eleven sectors is called "Cylinder" or "Track", and there exist 80 
  5721. cylinders on each side of the disk. In total you can store 512 (bytes/sector) * 
  5722. 11 (sectors/cylinder) * 80 (cylinders/side) * 2 (double sided disks) = 901120 
  5723. bytes = 880KB (901120 / 1024). 
  5724.  
  5725. The trackdisk device will only handle whole cylinders. Even if you only want to 
  5726. read some sectors of a cylinder the complete cylinder will be loaded into the 
  5727. trackdisk's own memory buffer. This technique of reading and writing complete 
  5728. cylinders greatly improves the storage capacity and speed of the drives. The 
  5729. actual reading and writing process is very quick compared to the time it takes 
  5730. for the drive to move the head to the right position and start rotating the 
  5731. disk.
  5732.  
  5733. Your program will not notice that the trackdisk device is only using complete 
  5734. cylinders. If you want to read some sectors, the trackdisk device will read a 
  5735. complete cylinder, but gives you only those sectors you wanted. If you later 
  5736. want to read some sectors more it may happen that the device immediately can 
  5737. give you the data you wanted without having to access the disk.
  5738.  
  5739. 6.3  TRACKDISK DEVICE
  5740.  
  5741. The trackdisk device is controlled in the same manner as all other devices. You 
  5742. send your commands to it with help of a request block, and the device will send 
  5743. messages back to a specific reply port. So, to use the trackdisk device you 
  5744. have to: (Same as with most other devices.)
  5745.  
  5746. 1. Create a message port with which the device can send messages back to us. 
  5747. See chapter 1 "Devices" for more information.
  5748. 2. Allocate and preinitialize a request block.
  5749. 3. Open the trackdisk device.
  5750.  
  5751. 6.3.1  REQUESTBLOCK
  5752.  
  5753. A normal IOStdReq structure ("standard request block") is in most cases 
  5754. possible to use, but if you want to use some special features described further 
  5755. down you have to use the extended request block "IOExtTD". This IOExtTD 
  5756. structure is declared in header file "devices/trackdisk.h" as:
  5757.  
  5758. struct IOExtTD
  5759. {
  5760.   struct IOStdReq iotd_Req;
  5761.   ULONG iotd_Count;
  5762.   ULONG iotd_SecLabel;
  5763. };
  5764.  
  5765. iotd_Req:
  5766.     
  5767. The top part of the request block consists of a standard request block 
  5768. (IOStdReq structure).
  5769.  
  5770. iotd_Count:
  5771.     
  5772. Each disk drive you are working with is assigned a counter value which is 
  5773. increased each time a disk is removed. If you are using the "extended" commands 
  5774. (described below) the trackdisk device will check the driver's count value with 
  5775. this value. If the counter is greater than this value the request will be 
  5776. aborted.
  5777.                
  5778.     
  5779. If you are going to working with a disk it is best to first get the driver's 
  5780. current count value and store it here. You will then be sure that the user will 
  5781. not change disks without you knowing about it.
  5782.                
  5783. iotd_SecLabel:
  5784.     
  5785. As described above, each sector has a 16 bytes long "label" are. This label 
  5786. area is not used by the track disk device. However, if you want to read and/or 
  5787. write to this label area you should use the "extended" read and write commands 
  5788. described below, and this field should contain a pointer to some memory where 
  5789. the label data can be stored/read from.
  5790.                
  5791.     
  5792. NOTE! If you intend to read some sectors' label areas you must make sure you 
  5793. have allocated a buffer that is large enough. 16 bytes is needed for every 
  5794. sector you intend to read. If you want to write new sector labels the memory 
  5795. must of course also be large enough, but also initialized with the values you 
  5796. want to write.
  5797.  
  5798. Since this is an extended request block you have to use the CreateExtIO() 
  5799. function rather than CreateStdIO().
  5800.  
  5801. Synopsis
  5802. :
  5803.     
  5804. ext_req = CreateExtIO( msg_port, size );
  5805.  
  5806. ext_req:
  5807.     
  5808. (struct IORequest *) Pointer to the new extended request block, or NULL if the 
  5809. request block could not be created.
  5810.  
  5811. msg_port:
  5812.     
  5813. (struct MsgPort *) Pointer to the message port the device should use to 
  5814. communicate with you.
  5815.  
  5816. size:
  5817.     
  5818. (long) The number of bytes that should be allocated for the extended request 
  5819. block. Use the function sizeof() to find the exact number of bytes needed.
  5820.  
  5821. If you are not going to use the "extended" commands which are described below, 
  5822. but only the normal commands, you do not need to use this extended request 
  5823. block. A normal standard request block will be enough. However, if you later 
  5824. would change your program so it starts to use the extended commands you have to 
  5825. remember to also change the type of request block. Usually it is best to always 
  5826. use an extended request block, although it sometimes it not strictly necessary.
  5827.  
  5828. 6.3.2  OPEN THE TRACKDISK DEVICE
  5829.  
  5830. Once you have opened a (reply) message port and created the request block you 
  5831. can open the trackdisk device. This is done with help of the now for us famous 
  5832. OpenDevice() function.
  5833.  
  5834. Synopsis
  5835. :
  5836.     
  5837. error = OpenDevice( name, unit, req, flags );
  5838.  
  5839. error:
  5840.     
  5841. (long) If OpenDevice() managed to open the device it returns 0, else an error 
  5842. number is returned.
  5843.  
  5844. name:
  5845.     
  5846. (char *) Name of the device you want to open. The name of the trackdisk device 
  5847. is (surprise) "trackdisk.device". This name has been defined is the header file 
  5848. as "TD_NAME".
  5849.             
  5850.     
  5851. When you are opening devices you should always try to use the defined names 
  5852. rather than the actual names. If Commodore would some day change the name of 
  5853. the device (very unlikely though) you only have to recompile your program with 
  5854. the new header files, and your program will work again.
  5855.  
  5856. unit:
  5857.     
  5858. (long) Which disk drive you want to use. All Amigas are sold with at leas one 
  5859. internal drive (df0:), but many have bought at lest one more drive. In total 
  5860. there may exist up to four disk drivers. Drive df0: has been given the unit 
  5861. number 0, df1: the number 1, and so on...
  5862.             
  5863.     
  5864. If you are trying to access a drive which does not exist, OpenDevice() will 
  5865. fail. See Example 2.
  5866.             
  5867. req:
  5868.     
  5869. (struct IORequest *) Pointer to the request block you have created and 
  5870. initialized.
  5871.  
  5872. flags:
  5873.     
  5874. (long) This field is ignored by the trackdisk device.
  5875.  
  5876. 6.3.3  CLEAN UP
  5877.  
  5878. As always, do NOT forget to clean up after yourself! All opened message ports, 
  5879. request blocks, devices etc. must be closed and removed before your program may 
  5880. terminate. Here is what you have to do:
  5881.  
  5882. 1. Make sure that all your requests have been completed or aborted.
  5883. 2. Close the trackdisk device with help of the CloseDevice() function.
  5884.  
  5885. Synopsis
  5886. :
  5887.     
  5888. CloseDevice( req );
  5889.  
  5890. reg:
  5891.     
  5892. (struct IORequest *) Pointer to the device's request block.
  5893.  
  5894. 3. Delete all request blocks you have created. Note that if you have allocated 
  5895. extended request blocks you must use the DeleteExtIO() function, but if you 
  5896. have allocated standard request blocks you can use the DeleteStdIO() function. 
  5897. As recommended above you should only use the extended request blocks with the 
  5898. trackdisk device.
  5899.  
  5900. Synopsis
  5901. :
  5902.     
  5903. DeleteExtIO( std_req, size );
  5904.  
  5905. std_req:
  5906.     
  5907. (struct IOStdReq *) Pointer to the extended request block you want to delete.
  5908.  
  5909. size:
  5910.     
  5911. (long) The size of the request block, in bytes.
  5912.  
  5913. 4. Close all message ports you have opened. Do this by calling the DeletePort() 
  5914. function. Note that all messages still left at the port must be removed before 
  5915. you may close the message port.
  5916.  
  5917. Synopsis
  5918. :
  5919.     
  5920. DeletePort( msg_port );
  5921.  
  5922. msg_port:
  5923.     
  5924. (struct MsgPort *) Pointer to the MsgPort structure that should be deallocated.
  5925.  
  5926. 6.4  COMMANDS
  5927.  
  5928. When the trackdisk device has successfully been opened you may start to send 
  5929. requests to it. The trackdisk device can do a lot of useful stuff, so there 
  5930. exist several commands that could be used. These commands can be divided into 
  5931. two groups. The first group consists of the "normal" commands. These commands 
  5932. will not check if the disk has been removed or changed. Note also that these 
  5933. commands can not be used to read or write the label areas of the sectors.
  5934.  
  5935. The second group consists of the "extended" commands - those commands that 
  5936. needs the extended IOExtTD structure. These commands will check the unit 
  5937. counter too see if the disk has been changed, and if so, the commands will 
  5938. automatically be aborted. These commands can be used if you want to access the 
  5939. sectors' label areas.
  5940.  
  5941. When you want to read and write data you have to give the "io_Data" field of 
  5942. the request block a pointer to some memory where all data you read will be 
  5943. placed, or if you are writing, where the data that should be written is stored. 
  5944. This buffer should be "word aligned" which means that it must start on an even 
  5945. byte address. The memory must also be of the type "Chip Memory" since the 
  5946. blitter will be used to speed up the operations. To fulfill both demands you 
  5947. should allocate the memory with help of the AllocMem function. See examples 
  5948. further down for more information.
  5949.  
  5950. The "io_Offset" value contains the offset value from which the trackdisk device 
  5951. will start to read/write from. The correct offset value is calculated with this 
  5952. formula:
  5953.  
  5954. TD_SECTOR*( NUMSECS * 2 * cylinder + NUMSECS * head + sector )
  5955.  
  5956. The words in capital letters are defined in the header file 
  5957. "devices/trackdisk.h" as:
  5958. TD_SECTOR   512 (512 bytes / sector)
  5959. NUMSECS      11 (11 sectors / cylinder)
  5960.  
  5961. For example, if you want to start to read at side 0, cylinder 5, and sector 3, 
  5962. you should set the offset value to:
  5963. TD_SECTOR * ( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
  5964.  
  5965. You tell the trackdisk device to read/write a specific number of bytes by 
  5966. setting the "io_Length" field to the number of bytes that should be 
  5967. transferred. Note that you must read complete sectors, and the length should 
  5968. therefore be set to:
  5969. TD_SECTOR * "nr of desired sectors". (The trackdisk device will only read 
  5970. complete cylinders, but has nothing to do with this restriction of only reading 
  5971. complete sectors.)
  5972.  
  5973. The "io_Actual" filed will contain the number of actual bytes written/read. If 
  5974. this value is not the same as the "io_Length" something has happened. Check the 
  5975. "io_Error" field to find any error values. See below for more inforamtion about 
  5976. error messages.
  5977.  
  5978. 6.4.1  READ
  5979.  
  5980. Probably one of the most commonly used commands is undoubtedly the read 
  5981. command. If you simply want to read some data, and you do not care if the user 
  5982. has changed disks, you should use the "CMD_READ" command. However, most times 
  5983. it is best to check if the disk has been changed, before you start to read, and 
  5984. then you should use the extended "ETD_READ" command.
  5985.  
  5986. Here is an example: ("exreq" is a pointer to the request block, "buffer" is a 
  5987. pointer to some memory which is word aligned and is of the type chip memory. 
  5988. The buffer must at least be 2 * 512 bytes.)
  5989.  
  5990.   /* We want to read: */
  5991.   exreq->iotd_Req.io_Command = ETD_READ;
  5992.  
  5993.   /* Pointer to our buffer: */
  5994.   exreq->iotd_Req.io_Data = (APTR) buffer;
  5995.  
  5996.   /* Read two sectors: */
  5997.   exreq->iotd_Req.io_Length = TD_SECTORS * 2;
  5998.  
  5999.   /* Start to read at side 0, cylinder 5, and sector 3: */
  6000.   exreq->iotd_Req.io_Offset = (LONG)
  6001.    TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
  6002.  
  6003.   /* Do our request: */
  6004.   DoIO( exreq );
  6005.  
  6006. 6.4.2  WRITE
  6007.  
  6008. Same as with reading, there exist two commands to write data. The shorter 
  6009. "CMD_WRITE" will simply write to the disk which is currently in the drive. The 
  6010. extended "ETD_WRITE" will check that the disk has not been changed.
  6011.  
  6012. Here is a similar example as the one above. This time we will try to write two 
  6013. sectors of data:
  6014.  
  6015.   /* We want to write: */
  6016.   exreq->iotd_Req.io_Command = ETD_WRITE;
  6017.  
  6018.   /* Pointer to our buffer: */
  6019.   exreq->iotd_Req.io_Data = (APTR) buffer;
  6020.  
  6021.   /* Write two sectors: */
  6022.   exreq->iotd_Req.io_Length = TD_SECTORS * 2;
  6023.  
  6024.   /* Start to write at side 0, cylinder 5, and sector 3: */
  6025.   exreq->iotd_Req.io_Offset = (LONG)
  6026.    TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
  6027.  
  6028.   /* Do our request: */
  6029.   DoIO( exreq );
  6030.  
  6031. 6.4.3  MOTOR ON/OFF
  6032.  
  6033. You can tell the trackdisk device to start or stop the motor of the disk drive. 
  6034. Although you do not need to start the motor (this will be done automatically if 
  6035. you start to read or write), you must make sure to stop the drive after you 
  6036. have used it. You can either use the command "TD_MOTOR" or "ETD_MOTOR". The 
  6037. later will check that the disk has not been changed.
  6038.  
  6039. If the "io_Length" field is set to 0 the drive will be turned off. If the field 
  6040. is set to 1 the drive will be turned on. Here is an example ("exreq" is a 
  6041. pointer to the request block):
  6042.  
  6043.   /* Turn motor on/off: */
  6044.   exreq->iotd_Req.io_Command = ETD_MOTOR;
  6045.  
  6046.   /* Turn the motor on: (0 = off, 1 = on) */
  6047.   exreq->iotd_Req.io_Length = 1;
  6048.  
  6049.   /* Do our request: */
  6050.   DoIO( exreq );
  6051.  
  6052. Remember to always turn off the motor after you have used the drive! (If the 
  6053. motor already was on before you started to use it you do not need to turn it 
  6054. off. This means that some other device is also using the drive.)
  6055.  
  6056. 6.4.4  UPDATE THE DISK
  6057.  
  6058. When you write data to the disk it will not immediately be stored. The 
  6059. trackdisk device will as explained above only read and write complete 
  6060. cylinders, and if you do not write exactly an even number of cylinders, some 
  6061. data will be temporarily stored in the trackdisk device's internal memory 
  6062. buffer. When you later change cylinder the buffer will be copied out before the 
  6063. head is moved.
  6064.  
  6065. You can tell the trackdisk device to immediately copy the data in the buffer to 
  6066. the disk by sending a "CMD_UPDATE" or "ETD_UPDATE" command. The later command 
  6067. will as usual check if the disk has been removed or changed before the buffer 
  6068. is copied onto the disk. 
  6069.  
  6070. After you have written something to the disk and before you turn the motor off 
  6071. you should use this command. You will then be sure that all data you have 
  6072. written will actually be on the disk.
  6073.  
  6074. Here is an example:
  6075.   /* Update the disk (move any data still left in the buffer */
  6076.   /* to the disk:                                            */
  6077.   exreq->iotd_Req.io_Command = ETD_UPDATE;
  6078.  
  6079.   /* Do our request: */
  6080.   DoIO( exreq );
  6081. 6.4.5  CLEAR BUFFER
  6082.  
  6083. You can tell the trackdisk device to clear it's internal buffer by sending a 
  6084. "CMD_CLEAR" or "ETD_CLEAR" command. Be sure that you do not clear anything that 
  6085. still has not been moved out onto the disk.
  6086.  
  6087. This clear command is recommended to use after you have noticed that the disks 
  6088. have been changed. This will ensure that old data from another disk will not be 
  6089. written to the new disk.
  6090.  
  6091. Here is an example:
  6092.  
  6093.   /* Clear the temporary buffer: */
  6094.   exreq->iotd_Req.io_Command = ETD_CLEAR;
  6095.  
  6096.   /* Do our request: */
  6097.   DoIO( exreq );
  6098.  
  6099. 6.4.6  POSITION THE HEAD
  6100.  
  6101. You can move the head to a specific position by sending a "TD_SEEK" or 
  6102. "ETD_SEEK" command. The head will simply be moved to the specified position in 
  6103. the "io_Offset" field, but no data will be read or written. This command is 
  6104. usually not needed since the head will always be placed on the correct position 
  6105. when you read or write data.
  6106.  
  6107. Here is an example:
  6108.  
  6109.   /* Position the head: */
  6110.   exreq->iotd_Req.io_Command = ETD_SEEK;
  6111.  
  6112.   /* Move to position: side 1, cylinder 2, and sector 4: */
  6113.   exreq->iotd_Req.io_Offset = (LONG)
  6114.    TD_SECTOR*( NUMSECS * 2 * 2 + NUMSECS * 1 + 4 );
  6115.  
  6116.   /* Do our request: */
  6117.   DoIO( exreq );
  6118.  
  6119. 6.4.7  FORMAT
  6120.  
  6121. With help of the "TD_FORMAT" or "ETD_FORMAT" command you can format a cylinder. 
  6122. You give the "io_Data" field a pointer to some data which will be written onto 
  6123. the new cylinder, and set the "io_Length" to NUMSECS * TD_SECTOR (one 
  6124. cylinder). The "io_Offset" field should be set to an offset value to the 
  6125. cylinder you want to format.
  6126.  
  6127. If you write data to a cylinder and you receive a hard write error (the 
  6128. cylinder is not formatted), you can try to reformat that cylinder. Note that it 
  6129. is usually best to tell the user to use some other disk rather than trying to 
  6130. correct this one. The user can then try to use "Diskdoctor" or similar programs 
  6131. to solve the disk problem.
  6132.  
  6133. Here is an example:
  6134.  
  6135.   /* We want to format one cylinder: */
  6136.   exreq->iotd_Req.io_Command = ETD_FORMAT;
  6137.  
  6138.   /* Pointer to our buffer: (Must be at least one  */
  6139.   /* cylinder of bytes large, NUMSECS * TD_SECTOR. */
  6140.   exreq->iotd_Req.io_Data = (APTR) buffer;
  6141.  
  6142.   /* Format one cylinder: */
  6143.   exreq->iotd_Req.io_Length = NUMSECS * TD_SECTOR;
  6144.  
  6145.   /* Format cylinder 5 on side 0: */
  6146.   exreq->iotd_Req.io_Offset = (LONG)
  6147.    TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 );
  6148.  
  6149.   /* Do our request: */
  6150.   DoIO( exreq );
  6151.  
  6152. 6.4.8  REMOVE
  6153.  
  6154. You can send a "TD_REMOVE" command to the trackdisk device, and it will then 
  6155. increase the driver's count value. It will look like that the user has changed 
  6156. disks. 
  6157.  
  6158. Here is an example:
  6159.  
  6160.   /* Change disks: */
  6161.   exreq->iotd_Req.io_Command = TD_REMOVE;
  6162.  
  6163.   /* Do our request: */
  6164.   DoIO( exreq );
  6165.  
  6166. 6.4.9  GET THE DISK'S CURRENT COUNT NUMBER
  6167.  
  6168. The "extended" commands check if the disk has changed or not before they are 
  6169. executed. If the disk has changed the command is aborted. The commands compare 
  6170. the "iotd_Count" value of the request block with the disk's current count 
  6171. value. If the disk's count value is larger than the "iotd_Count" the command is 
  6172. aborted and the error flag "TDERR_DiskChanged" is set.
  6173.  
  6174. Before you use these extended commands you should therefore get the disk's 
  6175. current count value and store it in the request block. To get the disk's 
  6176. current count value you send an extended request block with the "TD_CHANGENUM" 
  6177. command set to the trackdisk device. The device will as soon as possible return 
  6178. your request with a copy of the current count value in the "io_Actual" field of 
  6179. the request block.
  6180.  
  6181. Here is an example:
  6182.  
  6183.   /* Store the current count value here: */
  6184.   ULONG count_value;
  6185.  
  6186.   /* Get the disk's change count value: */
  6187.   extreq->iotd_Req.io_Command = TD_CHANGENUM;
  6188.  
  6189.   /* Do our request, and return when completed: */
  6190.   DoIO( extreq );
  6191.   
  6192.   /* Save the count value in the variable:  */
  6193.   count_value = extreq->iotd_Req.io_Actual;
  6194.  
  6195. After you have got the current count value you should give it the the request 
  6196. blocks which should later be used. The request block can then use the extended 
  6197. commands. Example:
  6198.  
  6199.   /* Give the request block the current count value: */
  6200.   extreg->iotd_Count = count_value;
  6201.  
  6202. After your program has received a "TDERR_DiskChanged" you should try to get the 
  6203. new count value before you attempt to access the new disk.
  6204.  
  6205. 6.4.10  CHECK IF THERE IS A DISK IN THE DRIVE OR NOT
  6206.  
  6207. With help of the trackdisk device can you check if there is a disk in a drive 
  6208. or not. To do this you use the "TD_CHANGESTATE" command. If there is a disk in 
  6209. the drive will the "io_Actual" field of the request will contain zero. On the 
  6210. other hand, if there is not a disk in the drive the field will contain a 
  6211. non-zero value.
  6212.  
  6213.   /* Check if there is a disk in the drive or not: */
  6214.   extreq->iotd_Req.io_Command = TD_CHANGESTATE;
  6215.  
  6216.   /* Do our request, and return when completed: */
  6217.   DoIO( extreq );
  6218.  
  6219.   /* Is there a disk in the drive? */
  6220.   if( extreq->iotd_Req.io_Actual )
  6221.     printf( "No disk in the drive!\n" );
  6222.   else
  6223.     printf( "There is a disk in the drive!\n" );
  6224.  
  6225. 6.4.11  CHECK IF THE DISK IS WRITE PROTECTED OR NOT
  6226.  
  6227. It can sometimes be very useful to know if the disk is write protected or not. 
  6228. With help of the "TD_PROTSTATUS" command we can easily check this. If the disk 
  6229. is write protected the "io_Actual" field will contain a non zero value. On the 
  6230. other hand, if the disk is not write protected the field will contain zero.
  6231.  
  6232.   /* Check if the disk is write protected or not: */
  6233.   extreq->iotd_Req.io_Command = TD_PROTSTATUS;
  6234.  
  6235.   /* Do our request, and return when completed: */
  6236.   DoIO( extreq );
  6237.  
  6238.   /* Is the disk write protected or not? */
  6239.   if( extreq->iotd_Req.io_Actual )
  6240.     printf( "The disk is write protected!\n" );
  6241.   else
  6242.     printf( "The disk is not write protected!\n" );
  6243.  
  6244. 6.4.12  GET DRIVE TYPE
  6245.  
  6246. The trackdisk device can also be used to tell you what type of disk drives are 
  6247. connected. If you send a request block with the "TD_GETDRIVETYPE" command set, 
  6248. the "io_Actual" field of the request block will either contain the flag 
  6249. "DRIVE3_5" or "DRIVE5_25" when it is returned. The "DRIVE3_5" means that it is 
  6250. a normal 3 1/2" disk drive, and the "DRIVE5_25" means that it is a 5 1/4 (IBM) 
  6251. disk drive.
  6252.  
  6253.   /* Check drive type: */
  6254.   extreq->iotd_Req.io_Command = TD_GETDRIVETYPE;
  6255.  
  6256.   /* Do our request, and return when completed: */
  6257.   DoIO( extreq );
  6258.  
  6259.   /* What type is it? */
  6260.   if( extreq->iotd_Req.io_Actual == DISK3_5 )
  6261.     printf( "It is a normal 3 1/2\" disk drive.\n" );
  6262.   else
  6263.     if( extreq->iotd_Req.io_Actual == DISK5_25 )
  6264.       printf( "It is a 5 1/4\" disk drive.\n" );
  6265.     else
  6266.       printf( "It is a very strange disk drive!\n" );
  6267.  
  6268. 6.4.13  GET THE NUMBER OF TRACKS 
  6269.  
  6270. The "TD_GETNUMTRACKS" command is used to check how many tracks are used by the 
  6271. drive. When the request is returned you can check the "io_Actual" field of the 
  6272. request to get the number of tracks. A normal 3 1/2 disk drive uses 160 tracks 
  6273. (80 tracks / side).
  6274.  
  6275.   /* Get the number of tracks this drive is using: */
  6276.   extreq->iotd_Req.io_Command = TD_GETNUMTRACKS;
  6277.  
  6278.   /* Do our request, and return when completed: */
  6279.   DoIO( extreq );
  6280.  
  6281.   /* How many tracks? */
  6282.   printf( "No tracks: %d\n", extreq->iotd_Req.io_Actual );
  6283.  
  6284. 6.5  ERRORS
  6285.  
  6286. When you are using the trackdisk device you will most definitely receive many 
  6287. errors. The "io_Error" field of the requestblock will contain 0 if the request 
  6288. was successfully executed, but if something failed it will contain one of the 
  6289. following error numbers: (Defined in the header file "devices/trackdisk.h".)
  6290. Error code
  6291.     
  6292. Description
  6293.  
  6294. TDERR_NotSpecified
  6295.     
  6296. A strange error has occurred.
  6297. TDERR_NoSecHdr
  6298.     
  6299. Could not find a sector.
  6300. TDERR_BadSecPreamble
  6301.     
  6302. Strange sector.
  6303. TDERR_BadSecID
  6304.     
  6305. Also a strange sector.
  6306. TDERR_BadHdrSum
  6307.     
  6308. Strange header.
  6309. TDERR_BadSecSum
  6310.     
  6311. Strange data areas.
  6312. TDERR_TooFewSecs
  6313.     
  6314. Missing some sectors.
  6315. TDERR_BadSecHdr
  6316.     
  6317. Yet another strange sector.
  6318. TDERR_WriteProt
  6319.     
  6320. The disk is write protected.
  6321. TDERR_DiskChanged
  6322.     
  6323. The disk has been removed/changed.
  6324. TDERR_SeekError
  6325.     
  6326. Could not reach track zero.
  6327. TDERR_NoMem
  6328.     
  6329. Not enough memory.
  6330. TDERR_BadUnitNum
  6331.     
  6332. Wrong unit number.
  6333. TDERR_BadDriveType
  6334.     
  6335. Strange drive type.
  6336. TDERR_DriveInUse
  6337.     
  6338. Someone is already using the drive.
  6339. TDERR_PostReset
  6340.     
  6341. Tea time, the system is resetted.
  6342. There exist also four general device errors which all devices may use: (Defined 
  6343. in header file "exec/errors.h".)
  6344.  
  6345. IOERR_OPENFAIL
  6346.     
  6347. Could not open the device.
  6348. IOERR_ABORTED
  6349.     
  6350. Request aborted.
  6351. IOERR_NOCMD
  6352.     
  6353. Not a valid command.
  6354. IOERR_BADLENGTH
  6355.     
  6356. Bad length or value.
  6357.  
  6358. 6.6  EXAMPLES
  6359.  
  6360. Example 1
  6361. This program will use the Trackdisk Device to turn on and off the internal disk 
  6362. drive's motor.
  6363.  
  6364. Example 2
  6365. This program demonstrates how you can check what went wrong while you were 
  6366. using the Trackdisk Device. This example will try to use drive DF3:, which most 
  6367. of us does not have, and thus we will receive an error message. (Well if you 
  6368. have four disk drives connected to your Amiga there will not be any error 
  6369. message.)
  6370.  
  6371. Example 3
  6372. This example demonstrates how you can read data with help of the Trackdisk 
  6373. Device. You give this program four arguments (drive, head, cylinder and 
  6374. sector), and it will print out all data in that sector. You only have to expand 
  6375. this program a little and you will end up with a nice disk viewer.
  6376.  
  6377. Example3 drive (0-3) head (0-1) cylinder (0-79) sector (0-10)
  6378.  
  6379. Example 4
  6380. This example contains a lot of small and useful functions that does almost 
  6381. everything you ever would like to do with the trackdisk device. The example has 
  6382. been written so you can easily use the functions in your own programs.
  6383.  
  6384.  
  6385. SERIAL DEVICE
  6386.  
  6387. 7.1  INTRODUCTION
  6388.  
  6389. All Amiga models have a serial port to which you can connect external devices 
  6390. like a modem, scanner or a printer with a serial interface. Data can be sent in 
  6391. both directions, in several different formats, like seven or eight bits, with 
  6392. or without error checking etc.
  6393.  
  6394. On the Amiga the serial port consists of a 25 pin connector, and can 
  6395. communicate with baud rate up to 32250. It is the custom chip called "Paula" 
  6396. which contains a Universal Asynchronous Receiver/Transmitter (UART) that takes 
  6397. care of all serial data transmissions. The chip itself can manage up to one 
  6398. million bits per second, but at that speed the data buffer would be filled 
  6399. before the system had time to react, and normal cables can not manage to 
  6400. transmit more than 100,000 - 200,000 bits per second.
  6401.  
  6402. Baud rates between 1200 and 9600 are in most cases more than sufficient, and at 
  6403. that speed the computer will still be able to multitask properly.
  6404.  
  6405. 7.2  THE SERIAL PORT
  6406.  
  6407. The serial port sends and receives data in streams of bits. In theory only one 
  6408. physical wire would therefore be required, but there have been several extra 
  6409. cables added to send and receive special information as well as to supply 
  6410. external devices with power (+12V) and so on.
  6411.  
  6412. Since all data is sent bit by bit, and not as with the parallel port which 
  6413. sends one byte (8 bits) each time, it is rather slow. (Well at least not as 
  6414. fast as the parallel port, which is fully documented in the next chapter.) 
  6415. However, since some applications only works with one line, for example the 
  6416. modems which are connected to the telephone system, serial communication is 
  6417. easier to handle. Serial cables are also much cheaper.
  6418.  
  6419. Although only one line is required there is a whole 25-pin connector on the 
  6420. Amiga. Normal applications like a modem, or a scanner only needs some of the 
  6421. lines, but since there are so many possible lines to use, a lot of special 
  6422. equipment can be connected to the serial port. (See illustration "RS-232".)
  6423.  
  6424. 7.2.1  BYTE TO BITS AND VICE VERSA
  6425.  
  6426. Since the serial device only sends a stream of bits, a special chip has to 
  6427. convert the data (bytes) to bits (8). The character "A" would for example be 
  6428. transformed to 01000001 and "B" to 01000010 and so on. Of course the chip must 
  6429. also be able to do the opposite, to convert an incoming stream of bits to 
  6430. bytes.
  6431.  
  6432.             --------
  6433.   A (64) <- |      | <- 01000001  (Receiving data)
  6434.             | UART |
  6435.   D (68) -> |      | -> 01000100  (Transmitting data)
  6436.             --------
  6437.  
  6438. The special chip that takes care of this is the "Universal Asynchronous 
  6439. Receiver/Transmitter" usually referred to as "UART". (See illustration "UART") 
  6440. Luckily we do not need to bother too much about this. However, it is good to 
  6441. know what is actually happening.
  6442.  
  6443.  
  6444.  
  6445. 7.2.2  PIN ASSIGNMENT
  6446.  
  6447. The Amiga's serial port is a 25-pin D-female-type connector. Below is an almost 
  6448. complete list of the pin assignment, together with a short description. (See 
  6449. illustration "RS-232".)
  6450.  
  6451. Pin  Amiga   RS232  HAYES  Direction  Type      Description
  6452. -------------------------------------------------------------------
  6453.   1  SHIELD  GND    GND    -          Standard  Ground  
  6454.   2  TXD     TXD    TXD    Out        Standard  Transmit data
  6455.   3  RXD     RXD    RXD    In         Standard  Receive data
  6456.   4  RTS     RTS x  -      Out        Standard  Request to send
  6457.   5  CTS     CTS x  CTS    In         Standard  Clear to send
  6458.   6  DSR     DSR x  DSR    In         Standard  Data set ready
  6459.   7  GND     GND    GND    -          Standard  Signal ground
  6460.   8  DCD     DCD    DCD    In         Standard  Carrier detect
  6461.   9  +12V    -      -      -          Amiga     +12 Volt
  6462.  10  -12V    -      -      -          Amiga     -12 Volt
  6463.  11  AUDO    -      -      Out        Amiga     Audio output
  6464.  12  -       S.DS   SI     +          -         Speed indicate
  6465.  13          S.CTS  -      +          -         RS232 code
  6466.  14  -       S.TXD  -      +          -         RS232 code
  6467.  15  -       TXC    -      +          -         RS232 code
  6468.  16  -       S.RXD  -      +          -         RS232 code
  6469.  17  -       RXC    -      +          -         RS232 code
  6470.  18  AUDI    -      -      In         Amiga     Audio in (not used)
  6471.  19  -       S.RTS  -      +          -         RS
  6472.  20  DTR     DTR x  DTR    Out        Standard  Data terminal ready
  6473.  21  -       SQD    -      -          *         (A1000 +5V)
  6474.  22  RI      RI     RI     In         Standard  Ring indicator
  6475.  23  -       SS     -      -          *         (A1000 +12V)
  6476.  24  -       TXC1   -      -          -         3.58 MHz clock
  6477.  25  -       -      -      -          Amiga     Reset (A1000)
  6478.  
  6479. (x) Used only if you have set the "seven-wire flag". Se below for more 
  6480. information.
  6481.  
  6482. WARNING! Pin 21 and 23 are connected to the power supply on the old Amiga 1000s 
  6483. (+5V and +12V). On all other models the power is sent through pin 9 and 10. Be 
  6484. careful with this if you intend to make serial cables! We do not want to burn 
  6485. the user's external devices, do we?
  6486.  
  6487. 7.2.3  THE DATA STREAM
  6488.  
  6489. Since all data is sent and received in a stream of bits, the UART chip needs to 
  6490. know when each new byte (set of 8 bits) is coming in. To make this possible, 
  6491. following rules have been stated: (See illustration "Serial Bits".)
  6492.  
  6493. 1. When no data is sent, a mark bit (1) is sent over and over.
  6494. 2. Just before a byte is sent, a start bit (0) is transmitted. The receiver 
  6495. will now know that seven or eight bits (depends on what type of device it is) 
  6496. of data will be received.
  6497. 3. An optional "parity" bit will come directly after the data bits. The parity 
  6498. bit is used for error checking. The receiver may use "even", "odd" or no parity 
  6499. at all. If the receiver is using even parity, the sender should set the parity 
  6500. bit to 1 if the remainder of all data bits divided by two is 0, else the parity 
  6501. bit should be set to 0. If the receiver is using odd parity, the sender should 
  6502. set the parity bit to 0 if the remainder of all data bits divided by two is 0, 
  6503. else the parity bit should be set to 1. If the receiver does not use parity 
  6504. checking, either set the parity bit to 0 or do not send any parity bit at all.
  6505. 4. Finally we need to send one (or two) stop bit(s) to tell the receiver that 
  6506. the last data bit has been sent.
  6507.  
  6508.   ...1 0 0 0 1 0 0 0 1 0 1 0 0 0 1... -> -> ->
  6509.   ..._       _       _   _       _...
  6510.       \_____/ \_____/ \_/ \_____/             
  6511.      ^                           ^
  6512.      | ^ ^ ^ 8 7 6 5 4 3 2 1 0 ^ |
  6513.      | | | | <-- Data bits --> | Mark bit(s)
  6514.      | | | |   (7 or 8 bits)   | 
  6515.      | | | |                   Start bit
  6516.      | | | |                        
  6517.      | \ / Parity bit (We use even parity, hence 0)                 
  6518.      |  | 
  6519.      |  One or two stop bits
  6520.      |
  6521.      Mark bit(s)
  6522.  
  6523. Even parity:
  6524.     
  6525. The sum of the data bits is divided by two, and if the remainder is 0, the 
  6526. parity bit should be set to 1. If the remainder is 1, the parity bit should be 
  6527. set to 0. 
  6528.  
  6529.     
  6530. 10100110 = 4 bits are set. 4/2  remainder = 0 -> parity = 1
  6531.     
  6532. 01110110 = 5 bits are set. 5/2  remainder = 1 -> parity = 0
  6533.  
  6534.  
  6535. Odd parity:
  6536.     
  6537. The sum of the data bits is divided by two, and if the remainder is 0, the 
  6538. parity bit should be set to 0. If the remainder is 1, the parity bit should be 
  6539. set to 1. 
  6540.  
  6541.     
  6542. 10100110 = 4 bits are set. 4/2  remainder = 0 -> parity = 0
  6543.     
  6544. 01110110 = 5 bits are set. 5/2  remainder = 1 -> parity = 1
  6545.  
  6546. The nice thing about the serial device is that we do not need to bother about 
  6547. this. (So why did I write it?) You only have to tell the device if you want to 
  6548. use parity, and if so if it should use even (default) or odd parity. You should 
  6549. also tell the serial device if you want to send seven or eight data bits, and 
  6550. one or two stop bits. Once you have told the device what you want, you do not 
  6551. need to think about it any more.
  6552.  
  6553. 7.3  THE SERIAL DEVICE
  6554.  
  6555. The Serial Port is a limited resource it that sense that there exist (normally) 
  6556. only one port. There may however be several programs running at the same time 
  6557. which all want to use the serial port. To make this possible they all have to 
  6558. coordinate their work with the port. That is the serial device's main task.
  6559.  
  6560. You can tell the device that you want a shared access, which means that other 
  6561. programs may also use the serial port, or if you want exclusive access, no 
  6562. other programs may use the device. It is then up to the device if it will 
  6563. accept your demands or else deny you the right to use the serial port. (Another 
  6564. task may have exclusive access.)
  6565.  
  6566. Once you have been allowed to use the serial port, you should send all your 
  6567. demands to the serial device, and it will take care of everything. You only 
  6568. have to give it some information like how it should work (eight or seven bits 
  6569. data, parity? one or two stop bits, baud rate etc...) and where data should be 
  6570. fetched/stored.
  6571. 7.3.1  PREPARE THE SERIAL DEVICE
  6572.  
  6573. Before you should open the serial device you need to decide if you want 
  6574. exclusive or shared access, and if you want to use the special RTS/CTR, DTR/DSR 
  6575. ("seven-wire-mode").
  6576.  
  6577. 1. If you want shared access, other programs may also use the serial port, set 
  6578. the flag SERF_SHARED. (Se below.) If you want exclusive access you do not need 
  6579. to set any flags, since it is the default mode.
  6580.  
  6581. 2. If you want to use the RTS/CTR, DTR/DSR handshaking protocol 
  6582. ("seven-wire-mode") set the flag SERF_7WIRE.
  6583.  
  6584. 7.3.2  OPEN THE SERIAL DEVICE
  6585.  
  6586. As with all devices you have to open a message port through which the serial 
  6587. device can talk to you, and allocate a request block (a IOExtSer structure), 
  6588. before you may open the device itself.
  6589.  
  6590. 1. Open a message port: (Since it is only our task and the device that will use 
  6591. the message port, we do not need to make it "public", hence no name. Priority 
  6592. should as usual be set to 0, normal priority.)
  6593.  
  6594.      struct MsgPort *replymp;
  6595.  
  6596.      replymp = (struct MsgPort *)
  6597.        CreatePort( NULL, 0 );
  6598.  
  6599.      if( !replymp )
  6600.        clean_up( "Could not create the reply port!" );
  6601.  
  6602. 2. Allocate a request block of type IOExtSer structure. (The IOExtSer structure 
  6603. is an extended version of the normal request block, and should therefore be 
  6604. allocated with help of the CreateExtIO() function with the size set to sizeof( 
  6605. struct IOExtSer ).
  6606.  
  6607.      struct IOExtSer *serial_req;
  6608.  
  6609.      serial_req = (struct IOExtSer *)
  6610.        CreateExtIO( replymp, sizeof( struct IOExtSer ) );
  6611.  
  6612.      if( !serial_req )
  6613.        clean_up( "Not enough memory!" );
  6614.  
  6615. Once the message port and the request block have successfully been created, you 
  6616. should set the desired flags which were explained above, and then open the 
  6617. serial device.
  6618.  
  6619. 3. Set desired flags (only SERF_SHARED and SERF_7WIRE are accepted), and open 
  6620. the device.
  6621.  
  6622.      UBYTE error;
  6623.  
  6624.      serial_req->io_SerFlags = SERF_SHARED|SERF_7WIRE;
  6625.      error = OpenDevice( SERIALNAME, 0, serial_req, 0 );
  6626.  
  6627.      if( error )
  6628.        clean_up( "Could not open the Serial Device!" );
  6629. 7.3.3  SET SERIAL PARAMETERS
  6630.  
  6631. Once the serial device has successfully been opened you should set desired 
  6632. parameters like baud rate, seven or eight data bits and so on. To set these 
  6633. parameters you initialize the request block as described below, set the flag 
  6634. SDCMD_SETPARMS and tells EXEC to execute your request with a DoIO() command.
  6635.  
  6636. Here is a list of parameters that should be initialized: (We assume you have 
  6637. already opened the device as described above, and have also created an IOExtSer 
  6638. structure with a pointer named "ioreq" to it.
  6639.  
  6640. 1. The serial device collects data and stores it in a temporary buffer. Because 
  6641. of this it can read a lot of data without any interruption, and once there is a 
  6642. break the data in this temporary buffer is moved to the real data buffer. The 
  6643. temporary buffer should be at least 512 bytes, but more is recommended, 
  6644. especially if you want to collect a lot of data at high speed.
  6645.      
  6646. This temporary buffer is automatically created by the serial device, and also 
  6647. automatically deallocated when you close the device. If you have set one buffer 
  6648. size, and later on changes the size, the old buffer will be removed and a new 
  6649. buffer allocated. All data will then be lost, so do not change the size of the 
  6650. buffer while you are still collecting data.
  6651.  
  6652. The size is set in the io_RBufLen field of the request structure. The size must 
  6653. be at least 512, but other recommended sizes are 1024, 2048, 4096, 8192 or 
  6654. 16384. To set the internal input buffer size to 8192 bytes do like this:
  6655.  
  6656.        ioreq->io_RBufLen = 8192;
  6657.  
  6658. 2. You must tell the serial device at what speed it should communicate (how 
  6659. many bits per second). It is important that both the sender and receiver are 
  6660. using the same speed.
  6661.      
  6662. You set the desired speed by initializing the io_Baud field to one of the 
  6663. following baud rates (you may use other speeds, but most applications expect 
  6664. you to use on of the recommended speeds, and if it is to fast data can be lost 
  6665. or corrupted): 110, 300, 1200, 2400, 4800, 9600, 19200 or 31250. To set the 
  6666. baud rate to 9600 do like this:
  6667.   
  6668.        ioreq->io_Baud = 9600;
  6669.   
  6670. 3. Your program or the external device you are communicating with can send a 
  6671. "break message", which will pause the transmission for a specified time. This 
  6672. is useful if you have received to many messages and you can not answer all of 
  6673. them before more are arriving. A small break would in this case be appreciated. 
  6674. A break signal simply stops the communication for a specified number of 
  6675. microseconds.
  6676.      
  6677. You can set the number of microseconds the communication should be down after a 
  6678. break signal by initializing the io_BrkTime field. To set the break time to 
  6679. half a second do like this:
  6680.      
  6681.        ioreq->io_BrkTime = 500000;
  6682.   
  6683. 4. If you want to read normal ASCII characters you only need the first seven 
  6684. bits, and should therefore only try to collect seven data bits instead of 
  6685. eight. However, sometimes you want to receive whole bytes, and should then 
  6686. collect all eight data bits.
  6687.      
  6688. To set the number of bits you want to read each time you initialize the 
  6689. io_ReadLen field accordingly. To read eight data bits do like this: 
  6690.        ioreq->io_ReadLen = 8;
  6691.  
  6692. 5. Since you sometimes only want to send 7 data bits, but other times want send 
  6693. all 8 data bits, you should initialize the io_WriteLen field accordingly. To 
  6694. write eight bits do like this:
  6695.      
  6696.        ioreq->io_WriteLen = 8;
  6697.   
  6698. 6. After each character you need to send one or two stop bit(s), depending on 
  6699. what the other device expect. Normally you only use one stop bit, but if you 
  6700. are using seven data bits it happens that you should use two stop bits. To tell 
  6701. the serial device to use one stop bit to like this:
  6702.      
  6703.        ioreq->io_StopBits = 1;
  6704.  
  6705. 7. There exist some special modes that are selected by setting some serial 
  6706. flags. Here is a complete list of possible flags together with a short 
  6707. description:
  6708.      
  6709. SERF_XDISABLED
  6710.     
  6711. Set this flag if you want to turn off the XON-XOFF feature. Default is on.
  6712.  
  6713. SERF_EOFMODE
  6714.     
  6715. Set this flag if you want the Serial Device to send/collect data until it finds 
  6716. one of the eight specified "end-of-file" characters. (See below for more 
  6717. information.)
  6718.                        
  6719. SERF_SHARED
  6720.     
  6721. Set this flag if you want to share the serial port with other programs. Note 
  6722. that this flag should only be used BEFORE you have opened the device, and 
  6723. should NOT be changed later on. If you want to change status you should close 
  6724. the serial device, alter the status and then try to open it again.
  6725.                        
  6726. SERF_RAD_BOOGIE
  6727.     
  6728. Set this flag if you want to use the serial port at high speed. When this flag 
  6729. is set no parity is used, xON/xOFF handling is turned off, no break signals are 
  6730. allowed, and finally only eight-bit characters are used. When you need fast 
  6731. communication set this flag, but remember that the data will then not be error 
  6732. checked, and break signals are ignored.
  6733.  
  6734. SERF_QUEUEDBRK
  6735.     
  6736. Set this flag if you want break commands to be queued along with all other 
  6737. signals. The default is that a break command interrupts the process 
  6738. immediately.
  6739.  
  6740. SERF_7WIRE
  6741.     
  6742. If the flag is set, the seven-wire "handshaking" mode will be used. (Default is 
  6743. three-wire.) With this flag set you can use the RS232 RTS/CTS, DTR/DSR 
  6744. commands. This flag should only be used BEFORE when you have opened the serial 
  6745. device, and should NOT be changed after that. To change this mode, you must 
  6746. close the device, set the desired mode and then try to open it again.
  6747.  
  6748. SERF_PARTY_ODD
  6749.     
  6750. Set this flag if you want to use odd parity. (The default setting is even 
  6751. parity.)
  6752.  
  6753. SERF_PARTY_ON
  6754.     
  6755. Parity checking/writing is turned on. (The sum of all data bits are divided by 
  6756. two, and the remainder is the parity bit. If even parity is used the bit will 
  6757. be set to 1 if the remainder is even. If odd parity is used the parity bit will 
  6758. be set to 0 if the remainder is even.
  6759. You set desired flags in the io_SerFlags filed of the request structure. If you 
  6760. want to set several flags, put a "|" ("binary-OR") between. To set the parity 
  6761. on, and use the end of character mode, do like this:
  6762.      
  6763.        ioreq->io_SerFlags = SERF_PARTY_ON | SERF_EOFMODE;
  6764.  
  6765. Note! If you do not want to set any flags, remember to set the field to 0. If 
  6766. you do not make sure it is empty, some flags might accidentally be set.
  6767.  
  6768. 8. There exist a special field where more serial flags can be used in the 
  6769. future. For the moment there exist only two flags for this field.
  6770.  
  6771. SEXTF_MSPON
  6772.     
  6773. Set this flag if you want to use mark-space parity rather than odd-even parity.
  6774.  
  6775. SEXTF_MARK
  6776.     
  6777. If this and the SEXTF_MSPON flag is set, it will Mark.
  6778.      
  6779. If you want to use the mark-space method, with mark, do like this:
  6780.  
  6781.        ioreq->io_ExtFlags = SEXTF_MSPON | SEXTF_MARK;
  6782.  
  6783. Note! If you do not want to set any of these flags, remember to set the field 
  6784. to 0!
  6785.   
  6786. 9. If you have set the flag "SERF_EOFMODE", the serial device will send/collect 
  6787. data until it finds one of eight specified "end-of-file" characters. These 
  6788. eight characters are stored in a IOTArray structure which look like this:
  6789.      
  6790.        struct IOTArray
  6791.        {
  6792.          ULONG TermArray0;
  6793.          ULONG TermArray1;
  6794.        };
  6795.  
  6796. Each ULONG data consists of four bytes, and in total you can store eight bytes 
  6797. (end-of-file characters) in the array. To make the checking routine efficient 
  6798. you must store the characters in descending order! To copy the desired 
  6799. characters into the array do like this:
  6800.      
  6801.        /* Here is an array with all EOF characters: */
  6802.        /* NOTE! They MUST be in descending order!   */
  6803.        UBYTE eof_char[8]={ 0x54, 0x32, 0x16, 0x15,
  6804.                            0x12, 0x03, 0x00, 0x00 };
  6805.  
  6806.        /* Declare a unsigned byte pointer: */
  6807.        UBYTE *ptr;
  6808.  
  6809.        /* Simple loop variable: */
  6810.        int loop;
  6811.  
  6812.        ...
  6813.  
  6814.        /* Get the address of the IOTArray: */
  6815.        ptr = (UBYTE *) &(ioreq->io_TermArray);
  6816.  
  6817.        /* Set all eight end of file characters: */
  6818.        for( loop=0; loop < 8; loop++ )
  6819.        {
  6820.          /* Copy character after character: */
  6821.          *ptr = eof_chars[ loop ];
  6822.  
  6823.          /* Step one byte foreward: */
  6824.          ptr++;
  6825.        }
  6826.  
  6827. Once you have set all desired parameters in the request block (IOExtSer 
  6828. structure) you set the IO command to SDCMD_SETPARAMSand tell the serial device 
  6829. to do your request by calling the DoIO() function. If something went wrong 
  6830. DoIO() will return with an error number, else 0 is returned which means that 
  6831. your request have successfully been executed. (See below for a complete list of 
  6832. error messages.)
  6833.  
  6834.   ioreq->IOSer.io_Command = SDCMD_SETPARAMS;
  6835.  
  6836.   error = DoIO( ioreq );
  6837.  
  6838. 7.3.4  READ DATA
  6839.  
  6840. When you want to read data from the serial port, you have to do four things. 
  6841. First you have to give the serial device a pointer to a buffer where all data 
  6842. which you read will be stored. (This memory buffer is not the same as the 
  6843. serial device's own temporary input buffer that was explained above.)
  6844.  
  6845. Secondly you have to tell the device how many bytes/characters you want to 
  6846. read. You do it by either telling the device exactly how many characters you 
  6847. want to read, or set the length to -1 which means you want to read continuously 
  6848. and stop first when an end-of-file characters have been found. (Remember to set 
  6849. the flag "SERF_EOFMODE", or the serial device will not check for termination 
  6850. characters.)
  6851.  
  6852. Thirdly you have to set the command flag "CMD_READ", which tells the serial 
  6853. device that you want to read data from the serial port.
  6854.  
  6855. Finally you tell the serial device to do your request. You can either put your 
  6856. program to sleep while the serial device is reading data with a DoIO() call, or 
  6857. you use the SendIO() function and continue to do something while all data is 
  6858. collected. There exist even a special "quick" mode which should be used if very 
  6859. fast communication is needed.
  6860.  
  6861. Here is an example on how to read data: (Your program will be put to sleep 
  6862. while the data is collected.)
  6863.  
  6864. error:
  6865.     
  6866. (UBYTE) is a simple unsigned byte variable.
  6867.  
  6868. ioreq:
  6869.     
  6870. (struct IOExtSer *) is a pointer to an IOExtSer structure.
  6871.  
  6872. data:
  6873.     
  6874. (BYTE) is a pointer to a block of memory where all data which is collected will 
  6875. be stored. Note that the serial device will not check if it sends more data 
  6876. than actually can be stored in the buffer. Innocent memory can be corrupted if 
  6877. you do not make the buffer big enough!
  6878.  
  6879.   /* We want to read some data: */
  6880.   ioreq->IOSer.io_Command = CMD_READ;
  6881.  
  6882.   /* Give the start address of our data buffer: */
  6883.   ioreq->IOSer.io_Data = data;
  6884.  
  6885.   /* We want to read 400 bytes/characters: (The buffer must */
  6886.   /* then be at least 400 bytes.)                           */ 
  6887.   ioreq->IOSer.io_Length = 400;
  6888.  
  6889.   /* Do our request: */
  6890.   error = DoIO( ioreq );
  6891.  
  6892.   /* Everything OK? */
  6893.   if( error )
  6894.     printf( "Problems while reading!\n" );
  6895.  
  6896. The program above will go to sleep while the data is collected from the serial 
  6897. port. However, sometimes you may want to do something while the data is fetched 
  6898. and not go to sleep. Instead of using the function DoIO() you should then use 
  6899. the asynchronous SendIO(). As explained in chapter 17 DEVICES, the SendIO() 
  6900. will not wait for the request to be completed.
  6901.  
  6902. To check if the request have been completed or not you can either use the 
  6903. function CheckIO(), or wait for a message to arrive at the request block's 
  6904. reply port.
  6905.  
  6906. Once the request have been completed you can look at the io_Error field of the 
  6907. request block to check if everything was OK. If the field is zero everything 
  6908. was executed without any problems, but if it is non zero the request failed.
  6909.  
  6910. Here is an example:
  6911.  
  6912.   /* Declare a pointer and set it to NULL: */
  6913.   struct IOExtSer *ptr = NULL;
  6914.  
  6915.   ...
  6916.   
  6917.  
  6918.   /* We want to read some data: */
  6919.   ioreq->IOSer.io_Command = CMD_READ;
  6920.  
  6921.   /* Give the start address of our data buffer: */
  6922.   ioreq->IOSer.io_Data = data;
  6923.  
  6924.   /* We want to read 400 bytes/characters: (The buffer must */
  6925.   /* then be at least 400 bytes.)                           */
  6926.   ioreq->IOSer.io_Length = 400;
  6927.  
  6928.   /* Do our request and return immediately: */
  6929.   SendIO( ioreq );
  6930.  
  6931.  
  6932.   /* As long as the pointer is not pointing to */
  6933.   /* the request we should stay in the loop:   */
  6934.   while( ptr == NULL )
  6935.   {
  6936.     ... do something ...
  6937.  
  6938.   
  6939.     /* Check if the request has been completed: (If the  */
  6940.     /* request has been completed CheckIO() will return  */
  6941.     /* a pointer to the request, else NULL is returned.) */
  6942.     ptr = CheckIO( ioreq );
  6943.   }
  6944.  
  6945.   /* Remove the request block's message. (The ptr and ioreq */
  6946.   /* are in this example identical, so it does not matter   */
  6947.   /* whichever you will use. The parenthesis around the     */
  6948.   /* expression is actually unnecessary, but this looks     */
  6949.   /* better.)                                               */
  6950.   Remove( &(ptr->IOSer.io_Message.mn_Node) );
  6951.  
  6952.   /* Everything OK? Check the io_Error filed: */
  6953.   if( ioreq->IOSer.io_Error )
  6954.     printf( "Problems while reading!\n" );
  6955.  
  6956. Finally their exist a special "quick" mode. It can only sometimes be used and 
  6957. only for reading. This special modecan be used when a lot of data at a very (!) 
  6958. high speed should be collected. The "quick" mode is not the same as the 
  6959. "SERF_RAD_BOOGIE" mode explained above. The quick mode can only be used for 
  6960. reading, and there are some special restrictions, while the high speed rad 
  6961. boogie mode can be used both for reading and writing.
  6962.  
  6963. To use the quick mode you set the IOF_QUICK flag, before you send the request. 
  6964. You should then not use the normal DoIO() or SendIO() functions, but instead 
  6965. the low level BeginIO() function. The BiginIO() is synchronous (like DoIO()) if 
  6966. we were allowed to use quick mode, else the request is asynchronous (like 
  6967. SendIO()).
  6968.  
  6969. Although you want to use the quick mode, it is not sure you are allowed to do 
  6970. it. For some requests it is OK, others will automatically remove the IOF_QUICK 
  6971. flag. You must therefore check the request after you have posted it to see if 
  6972. the IOF_QUICK flag is still on or not. If the flag was removed, the quick mode 
  6973. was turned off and the request should now be treated as those started with a 
  6974. SendIO() call (remember to remove the message). However, it the flag is still 
  6975. there the quick mode was used and we do not need (nor should) remove any 
  6976. message at the reply port.
  6977.  
  6978.   /* Initialize the request block as normal. */
  6979.   /* ...                                     */
  6980.  
  6981.   /* Try to use the "quick" mode: */
  6982.   ioreq->IOSer.io_Flags = IOF_QUICK;
  6983.  
  6984.   /* Do the request: */
  6985.   BeginIO( ioreq );
  6986.  
  6987.   if( (ioreq->IOSer.io_Flags & IOFQUICK) )
  6988.   {
  6989.     /* OK! The request was using quick mode, which means  */
  6990.     /* that our task was put to sleep while the data was  */
  6991.     /* fetched, and the request have now been completed.  */
  6992.     /* Since we are using quick mode there are no message */
  6993.     /* that should be removed.                            */
  6994.   }
  6995.   else
  6996.   {
  6997.     /* Too bad, we were not allowed to use the quick mode. */
  6998.     /* The request should now be treated as if it was      */
  6999.     /* started with a SendIO() function call. This request */
  7000.     /* is asynchronous - request returns immediately.      */
  7001.   
  7002.     /* We can either use the routine above with CheckIO()  */
  7003.     /* or put our task to sleep while we are waiting, by   */
  7004.     /* calling the WaitIO() function. The WaitIO()         */
  7005.     /* function will automatically remove the message, so  */
  7006.     /* we do not need to do anything more. However, if we  */
  7007.     /* used the function Wait(), we have to remove the     */
  7008.     /* messages ourself.                                   */
  7009.     WaitIO( ioreq );
  7010.   }
  7011.  
  7012. The "quick" mode can only sometimes be used:
  7013.  
  7014. 1. You may only read data. The "quick" mode is not allowed for writing.
  7015.  
  7016. 2. There is enough data in the internal input buffer so the request will 
  7017. immediately be satisfied.
  7018.  
  7019. 3. No other requests is using/waiting for the serial port.
  7020.  
  7021. 7.3.5  WRITE DATA
  7022.  
  7023. When you want to write data to the serial port it is almost the same procedure 
  7024. as when reading. The only difference is that you set the command flag 
  7025. "CMD_WRITE".
  7026.  
  7027. Here is an example on how to write data to the serial port: (Your program will 
  7028. be put to sleep while the data is moved to the serial port.)
  7029.  
  7030. error:
  7031.     
  7032. (UBYTE) is a simple unsigned byte variable.
  7033.  
  7034. ioreq:
  7035.     
  7036. (struct IOExtSer *) is a pointer to an IOExtSer structure.
  7037.  
  7038. data:
  7039.     
  7040. (BYTE) is a pointer to a block of memory where all data you want to send is 
  7041. stored.
  7042.  
  7043.   /* We want to send (write) some data: */
  7044.   ioreq->IOSer.io_Command = CMD_WRITE;
  7045.  
  7046.   /* Give the start address of our data buffer: */
  7047.   ioreq->IOSer.io_Data = data;
  7048.  
  7049.   /* We want to send 150 bytes/characters: */
  7050.   ioreq->IOSer.io_Length = 150;
  7051.  
  7052.   /* Do our request: */
  7053.   error = DoIO( ioreq );
  7054.  
  7055.   /* Everything OK? */
  7056.   if( error )
  7057.     printf( "Problems while writing!\n" );
  7058.  
  7059. The program above will go to sleep while the data is copied to the serial port. 
  7060. If you do not want the program to wait for the request to be completed you 
  7061. should use SendIO(). The procedure is very similar with reading.
  7062.  
  7063. To check if the request have been completed or not you can either use the 
  7064. function CheckIO(), or wait for a message to arrive at the request block's 
  7065. reply port.
  7066.  
  7067. Once the request have been completed you can look at the io_Error field of the 
  7068. request block to check if everything was OK. If the field is zero everything 
  7069. was executed without any problems, but if it is non zero the request failed.
  7070.  
  7071. Here is an example:
  7072.  
  7073.   /* Declare a pointer and set it to NULL: */
  7074.   struct IOExtSer *ptr = NULL;
  7075.  
  7076.   ...
  7077.   
  7078.  
  7079.   /* We want to send (write) some data: */
  7080.   ioreq->IOSer.io_Command = CMD_WRITE;
  7081.  
  7082.   /* Give the start address of our data buffer: */
  7083.   ioreq->IOSer.io_Data = data;
  7084.  
  7085.   /* We want to send 280 bytes/characters: */
  7086.   ioreq->IOSer.io_Length = 280;
  7087.  
  7088.   /* Do our request and return immediately: */
  7089.   SendIO( ioreq );
  7090.  
  7091.  
  7092.   /* As long as the pointer is not pointing to */
  7093.   /* the request we should stay in the loop:   */
  7094.   while( ptr == NULL )
  7095.   {
  7096.     ... do something ...
  7097.  
  7098.   
  7099.     /* Check if the request has been completed: (If the  */
  7100.     /* request has been completed CheckIO() will return  */
  7101.     /* a pointer to the request, else NULL is returned.) */
  7102.     ptr = CheckIO( ioreq );
  7103.   }
  7104.  
  7105.   /* Remove the requst block's message. (The ptr and ioreq */
  7106.   /* are in this example identical, so it does not matter  */
  7107.   /* whichever you will use. The parenthesis around the    */
  7108.   /* expression is actually unnecessary, but this looks    */
  7109.   /* better.)                                              */
  7110.   Remove( &(ptr->IOSer.io_Message.mn_Node) );
  7111.  
  7112.   /* Everything OK? Check the io_Error filed: */
  7113.   if( ioreq->IOSer.io_Error )
  7114.     printf( "Problems while writing!\n" );
  7115.  
  7116. You can not use the quick mode while you are sending data. If you need fast 
  7117. communication you should set the serial flag "SERB_RAD_BOOGIE". Note the 
  7118. restrictions mentioned above. (Only eight-bit characters, and no parity 
  7119. checking etc...)
  7120.  
  7121. Since you usually will want to read and write data at the same time, you have 
  7122. to use two separate request blocks. If you are using shared access mode you 
  7123. simply create two request blocks and open the serial device twice. (Do not 
  7124. forget to close both requests later on.) Here is an example:
  7125.  
  7126.   struct MsgPort *replymp;
  7127.   struct IOExtSer *serial_req_read;
  7128.   struct IOExtSer *serial_req_write;
  7129.   UBYTE error;
  7130.  
  7131.      
  7132.   /* We use only one reply message port: */
  7133.   replymp = (struct MsgPort *)
  7134.     CreatePort( NULL, 0 );
  7135.   if( !replymp )
  7136.     clean_up( "Could not create the reply port!" );
  7137.  
  7138.  
  7139.   /* Create the request block "read": */
  7140.   serial_req_read = (struct IOExtSer *)
  7141.     CreateExtIO( replymp, sizeof( struct IOExtSer ) );
  7142.   if( !serial_req_read )
  7143.     clean_up( "Not enough memory!" );
  7144.   serial_req_read->io_SerFlags = SERF_SHARED|SERF_7WIRE;
  7145.  
  7146.   /* Create the request block "write": */
  7147.   serial_req_write = (struct IOExtSer *)
  7148.     CreateExtIO( replymp, sizeof( struct IOExtSer ) );
  7149.   if( !serial_req_write )
  7150.     clean_up( "Not enough memory!" );
  7151.   serial_req_write->io_SerFlags = SERF_SHARED|SERF_7WIRE;
  7152.  
  7153.  
  7154.   /* Open the serial device for the read request: */
  7155.   error = OpenDevice( SERIALNAME, 0, serial_req_read, 0 );
  7156.   if( error )
  7157.     clean_up( "Could not open the serial device (Read)!" );
  7158.  
  7159.   /* Open the serial device for the write request: */
  7160.   error = OpenDevice( SERIALNAME, 0, serial_req_write, 0 );
  7161.   if( error )
  7162.     clean_up( "Could not open the serial device (Write)!" );
  7163.  
  7164. If you are using exclusive access mode you can of course not open the serial 
  7165. device twice. Instead we have to copy the first request block to the other 
  7166. request block, byte for byte. Here is an example:
  7167.  
  7168.   struct MsgPort *replymp;
  7169.   struct IOExtSer *serial_req_read;
  7170.   struct IOExtSer *serial_req_write;
  7171.   BYTE *r_ptr;
  7172.   BYTE *w_ptr;
  7173.   UBYTE error;
  7174.   int loop;
  7175.  
  7176.      
  7177.   /* We use only one reply message port: */
  7178.   replymp = (struct MsgPort *)
  7179.     CreatePort( NULL, 0 );
  7180.   if( !replymp )
  7181.     clean_up( "Could not create the reply port!" );
  7182.  
  7183.  
  7184.   /* Create the request block "read": */
  7185.   serial_req_read = (struct IOExtSer *)
  7186.     CreateExtIO( replymp, sizeof( struct IOExtSer ) );
  7187.   if( !serial_req_read )
  7188.     clean_up( "Not enough memory!" );
  7189.   serial_req_read->io_SerFlags = SERF_SHARED|SERF_7WIRE;
  7190.  
  7191.   /* Create the requestblock "write": */
  7192.   serial_req_write = (struct IOExtSer *)
  7193.     CreateExtIO( replymp, sizeof( struct IOExtSer ) );
  7194.   if( !serial_req_write )
  7195.     clean_up( "Not enough memory!" );
  7196.   serial_req_write->io_SerFlags = SERF_SHARED|SERF_7WIRE;
  7197.  
  7198.  
  7199.   /* Open the serial device for the read request: */
  7200.   error = OpenDevice( SERIALNAME, 0, serial_req_read, 0 );
  7201.   if( error )
  7202.     clean_up( "Could not open the Serial Device (Read)!" );
  7203.  
  7204.  
  7205.   /* Since we can not open the serial device once again */
  7206.   /* for the write request, we have to copy the whole   */
  7207.   /* read request block into the write request block.   */
  7208.  
  7209.   /* Get the start address of both request blocks: */
  7210.   r_ptr = (BYTE *) serial_req_read;
  7211.   w_ptr = (BYTE *) serial_req_write;
  7212.  
  7213.   /* Copy the request block, byte by byte: */
  7214.   for( loop=0; loop < sizeof( struct IOExtSer ); loop++ )
  7215.   {
  7216.     /* Copy one byte: */
  7217.     *w_ptr = *r_ptr;
  7218.     
  7219.     /* Step one byte forward: */
  7220.     w_ptr++;
  7221.     r_ptr++;
  7222.   }
  7223.  
  7224. Now we have two request blocks, and can therefore use both read and write 
  7225. requests at the same time. If you also want to use the special extra functions 
  7226. described further down, you may want to create even one more request block (or 
  7227. maybe even more...). The procedure is the same as described above.
  7228. 7.3.6  ERRORS
  7229.  
  7230. While you are using the serial device several errors may occur. The most common 
  7231. problem is that some other program is already using the serial port, but it can 
  7232. happen that data has been corrupted while reading/writing (the parity checking 
  7233. is a simple way to find these errors).
  7234.  
  7235. There exit for the moment nine different types of serial port errors, all 
  7236. defined in the header file "devices/serial.h". You will receive the error 
  7237. message from the function you just called, or you can check a request block to 
  7238. see if there were any problems. (The io_Error filed of the request block either 
  7239. contains 0, which means everything is OK, or an error number.)
  7240.  
  7241. Here is a complete list of the serial error messages:
  7242.  
  7243. SerErr_DevBusy:
  7244.     
  7245. Some other task/request is already using the serial device.
  7246.  
  7247. SerErr_BufErr:
  7248.     
  7249. The serial device could not allocate enough memory for the internal input 
  7250. buffer.
  7251.  
  7252. SerErr_InvParam:
  7253.     
  7254. The request block's parameters were not properly initialized.
  7255.  
  7256. SerErr_LineErr:
  7257.     
  7258. The serial cable is faulty or the other device is not properly initialized or 
  7259. not connected. Tell the user to check the cables!
  7260.  
  7261. SerErr_ParityErr:
  7262.     
  7263. The data you just sent/received contained at least one byte which was 
  7264. corrupted. Note that the serial flag "SERF_PARITY_ON" must be set, else the 
  7265. device will not check for errors.
  7266.  
  7267. SerErr_TimerErr:
  7268.     
  7269. There was some problem with the timer.
  7270.  
  7271. SerErr_BufOverflow:
  7272.     
  7273. The serial input buffer has been filled. You should empty it as fast as 
  7274. possible so you do not lose valuable information.
  7275.                       
  7276. SerErr_NoDSR:
  7277.     
  7278. No DSR.
  7279.  
  7280. SerErr_DetectedBreak:
  7281.     
  7282. A break was detected.
  7283.  
  7284. While you are using the serial device it may happen that you receive other 
  7285. error messages than described above. It is then usually the Exec which sent 
  7286. them: (defined in the header file "exec/errors.h")
  7287.  
  7288. IOERR_OPENFAIL
  7289.     
  7290. The device (unit) could not be opened. (If you are denied access to the serial 
  7291. device you should receive the SerErr_DevBusy flag instead of this exec message, 
  7292. but internally this flag is used.) 
  7293.  
  7294. IOERR_ABORTED
  7295.     
  7296. When you abort a previously started request by calling the AbortIO() function, 
  7297. the io_Error filed of that request is set to IOERR_ABORTED. If you find a 
  7298. request block with this flag set, you know that it has been aborted.
  7299.  
  7300. IOERR_NOCMD
  7301.     
  7302. You tried to use a command that is not supported by the serial device.
  7303.  
  7304. IOERR_BADLENGTH
  7305.     
  7306. T
  7307. he length of the request was not valid.
  7308.  
  7309.  
  7310. 7.3.7  CLEAN UP
  7311.  
  7312. As usual on the Amiga you must remember to close and return everything you have 
  7313. opened or allocated. If you do not close the serial device after you, other 
  7314. programs will then not be able to use the serial port. PLEASE be very careful 
  7315. about this!
  7316.  
  7317. Here is a list of what you have to do:
  7318.  
  7319. 1. All requests you have started with SendIO() or BeginIO() (asynchronous 
  7320. commands) must either have been completed or aborted before you may close the 
  7321. device. It is a very common error to forget this, and it can be hard to find 
  7322. this bug. Usually the program will work fine (the command was completed in 
  7323. time), but now and then your program will crash (the command was completed 
  7324. after the device have been closed).
  7325.  
  7326. A simple way is to abort all commands that have not reported that they have 
  7327. been completed, but this is not always good way to do it. (The last commands 
  7328. may be important and should therefore not be aborted.)
  7329.      
  7330. If you do not want to abort the command, you should instead wait for it to be 
  7331. completed. The WaitIO() function is simple to use, and will put your program to 
  7332. sleep while waiting, so no computer time is wasted. If the request has already 
  7333. been completed, the function will return immediately. WaitIO() will also remove 
  7334. the message from the reply port. It is a very useful and simple function to 
  7335. use, but do NOT try to wait for a request that has not been started! (It will 
  7336. then be a very long wait, probably some million years before the user realizes 
  7337. that he/she has to hit the reset keys.)
  7338.  
  7339. Here is an example on how to wait for a request to be completed: (If the 
  7340. request already has been completed it does not matter, WaitIO() will then 
  7341. simply return immediately. Note that we do not have to remove any messages from 
  7342. the reply port if we use WaitIO().)
  7343.  
  7344.        /* Store possible error numbers here: */
  7345.        UBYTE error;
  7346.      
  7347.        /* ... */
  7348.      
  7349.        /* Wait for the request to be completed: */
  7350.        error = WaitIO( ioreq );
  7351.      
  7352.        /* Everything OK? */
  7353.        if( error )
  7354.          printf( "Something went wrong!" );
  7355.  
  7356.        /* Well, successful or not, we may now */
  7357.        /* close the device!                   */
  7358.  
  7359. To abort a request, simply use the AbortIO() function:
  7360.  
  7361.        /* Try to abort a previously started request:  */
  7362.        /* (Do not try to abort a request that has not */
  7363.        /* been started.)                              */
  7364.        AbortIO( ioreq );
  7365.  
  7366. 2. When all requests have been completed or aborted you may close the serial 
  7367. device. (Requests that have been executed by calling the DoIO() function have 
  7368. already been completed before your program wakes up, and thus you do not need 
  7369. to wait for these.)
  7370.  
  7371. The serial device is closed as all other devices, by calling the CloseDevice() 
  7372. function. Here is an example:
  7373.  
  7374.        /* Close the Serial Device: */
  7375.        if( !serial_dever )
  7376.          CloseDevice( ioreq );
  7377.  
  7378. 3. You should now return all request blocks you have allocated. If you 
  7379. allocated a standard sized request block by calling the CreateStdIO() function, 
  7380. you should free it by calling the DeleteStdIO() function. However, if you have 
  7381. allocated an extended request block (like the serial device's request blocks) 
  7382. by calling the CreateExtIO() function, you MUST call the DeleteExtIO(), and of 
  7383. course remove the same amount of data as you allocated.
  7384.      
  7385. How to delete a standard request block: (Since it is of the standard size, you 
  7386. do not need to specify any size.)
  7387.      
  7388.        /* Deallocate a standard sized request block: */
  7389.        DeleteStdIO( ioreq );
  7390.  
  7391. With extended request block you have to specify the size as you did when you 
  7392. allocated it:
  7393.  
  7394.        /* Deallocate an extended request block: */
  7395.        /* (The size may vary depending on what  */
  7396.        /* device it was used for.)              */
  7397.        DeleteExtIO( ioreq, sizeof( struct IOExtSer ) );
  7398.  
  7399. Note that ALL request blocks that have been allocated, must be removed!
  7400.  
  7401. 4. Finally you should close all message ports you have previously opened. 
  7402. Simply use the DeletePort() function as this example demonstrates:
  7403.  
  7404.        /* Remove the replyport: */
  7405.        DeletePort( replymp);
  7406.  
  7407. It can not be said too often. Please be careful with how your program 
  7408. terminates! Your program should not only run fine, but it should also allow 
  7409. other programs to run after and simultaneously. Remember that your program must 
  7410. also be able to quit nice and neatly even if it had to terminate too early 
  7411. because of some fatale error. The cleaning up should only be done where it is 
  7412. needed, and if you have not allocated the memory or opened the device before 
  7413. your program quits, you should of course NOT try to free these resources! If 
  7414. you do the Amiga will most certainly crash! Too many programs contain this very 
  7415. annoying error.
  7416.  
  7417. The best way to manage this cleaning up routine is to write a separate function 
  7418. which checks each thing before it frees it. The idea is that you may call this 
  7419. routine at any time, and it will still manage to clear everything properly. 
  7420. Here is an example: (Note how we check each thing to see if it should be 
  7421. removed!)
  7422.  
  7423.   /* Close and return everything that has been */
  7424.   /* opened and allocated before we quit:      */
  7425.   void clean_up()
  7426.   {
  7427.     /* 1. Close the Serial Device: */ 
  7428.     if( !serial_dever )
  7429.       CloseDevice( serial_req );
  7430.   
  7431.     /* 2. Deallocate the serial request block: */
  7432.     if( serial_req )
  7433.       DeleteExtIO( serial_req, sizeof( struct IOExtSer ) );
  7434.   
  7435.     /* 3. Remove the replyport: */
  7436.     if( replymp )
  7437.       DeletePort( replymp);
  7438.   
  7439.     /* 4. Quit: */
  7440.     exit( 0 );
  7441.   }
  7442.  
  7443. Comments:
  7444. 1. The serial_dever is a variable that we have declared ourself, and is used 
  7445. only to determine if the device has been opened or not. (The variable is set to 
  7446. TRUE before the program starts, and is only changed to FALSE after a successful 
  7447. opening of the device. See the included examples for more information.) We 
  7448. should of course only close the device if the serial_dever is FALSE. 
  7449. ("serial_dever" stands for "serial device error".)
  7450.  
  7451. 2. The serial_req is a pointer which is either pointing to a request block, or 
  7452. NULL if no request block has been allocated. We should of course only free the 
  7453. request block if there exist one.
  7454.  
  7455. 3. The replymp is a pointer which is either pointing to a message port, or NULL 
  7456. if no message port has been opened. And of course, we should only close the 
  7457. message port if it has been opened.
  7458.  
  7459. 4. Finally our program may terminate. The exit() function is placed here so we 
  7460. are sure the  program will quit. (If we did not have this exit(), our function 
  7461. would then return the control to our program again, and then we would have 
  7462. serial (sorry, serious) problems.
  7463.  
  7464. 7.4  A COMPLETE EXAMPLE
  7465.  
  7466. Here is a complete example that opens the serial device, sets all necessary 
  7467. parameters, sends some data, collects some data and finally cleans up and 
  7468. quits. Since we send the read and write requests after each other (the first is 
  7469. completed before the other is started) we only need to use one request block.
  7470.  
  7471. #include <exec/types.h>
  7472. #include <devices/serial.h>
  7473.  
  7474.  
  7475. /* Declare a pointer to our reply port: */
  7476. struct MsgPort *replymp = NULL;
  7477.  
  7478. /* Declare a pointer to our serial request block: */
  7479. struct IOExtSer *serial_req = NULL;
  7480.  
  7481. /* Store the serial device error here: */
  7482. UWORD serial_dever = TRUE;
  7483.  
  7484. /* Declare our own data buffer: (Big enough to hold 300 bytes.) */
  7485. BYTE buffer[ 300 ];
  7486.  
  7487.  
  7488. /* Declare our functions: */
  7489. void main();
  7490. void clean_up( STRPTR text );
  7491.  
  7492. void main()
  7493. {
  7494.   int loop;
  7495.   UBYTE *ptr;
  7496.   UBYTE error;
  7497.  
  7498.   /* The eight end-of-file characters: */
  7499.   UBYTE eof_char[8]={ 0x06, 0x05, 0x04, 0x03,
  7500.                       0x02, 0x01, 0x00, 0x00 };
  7501.  
  7502.  
  7503.   
  7504.   /* OPEN THE SERIAL DEVICE: */
  7505.  
  7506.   /* Get a reply port: (No name, priority 0) */
  7507.   replymp = (struct MsgPort *)
  7508.     CreatePort( NULL, 0 );
  7509.   if( !replymp )
  7510.     clean_up( "Could not create the reply port!" );
  7511.  
  7512.   /* Create a serial request block: */
  7513.   serial_req = (struct IOExtSer *)
  7514.     CreateExtIO( replymp, sizeof( struct IOExtSer ) );
  7515.   if( !serial_req )
  7516.     clean_up( "Not enough memory for the serial request block!" );
  7517.  
  7518.   /* Open the Serial Device: */
  7519.   serial_dever = OpenDevice( SERIALNAME, 0, serial_req, 0 );
  7520.   if( serial_dever )
  7521.     clean_up( "Could not open the Serial Device!" );
  7522.  
  7523.  
  7524.  
  7525.   
  7526.   /* SET THE SERIAL PARAMETERS: */
  7527.   
  7528.   /* Set the Serial Device's own input buffer to 512 bytes: */
  7529.   serial_req->io_RBufLen = 512;
  7530.   
  7531.   /* Set baud rate to 9600 baud: */
  7532.   serial_req->io_Baud = 9600;
  7533.   
  7534.   /* Set break time to half a second: */
  7535.   serial_req->io_BrkTime = 500000;
  7536.   
  7537.   /* Read 8 bits per character: */
  7538.   serial_req->io_ReadLen = 8;
  7539.  
  7540.   /* Write 8 bits per character: */
  7541.   serial_req->io_WriteLen = 8;
  7542.   
  7543.   /* Use 1 stop bit: */
  7544.   serial_req->io_StopBits = 1;
  7545.  
  7546.   /* Use parity and end-of-file characters: */
  7547.   serial_req->io_SerFlags = SERF_PARTY_ON | SERF_EOFMODE;
  7548.  
  7549.   /* No additional flags: */
  7550.   serial_req->io_ExtFlags = NULL;
  7551.   
  7552.   /* Set all eight end of file characters: */
  7553.   ptr = (UBYTE *) &(serial_req->io_TermArray);
  7554.   for( loop=0; loop < 8; loop++ )
  7555.   {
  7556.     /* Copy character after character: */
  7557.     *ptr = eof_char[ loop ];
  7558.     /* Step one byte forward: */
  7559.     ptr++;
  7560.   }
  7561.   
  7562.   /* All values have now been set, lets do a SDCMD_SETPARAMS request: */
  7563.   serial_req->IOSer.io_Command = SDCMD_SETPARAMS;
  7564.   
  7565.   /* Do our request: */
  7566.   error = DoIO( serial_req );
  7567.   if( error )
  7568.     clean_up( "Could not set the serial parameters!" );
  7569.  
  7570.  
  7571.  
  7572.   /* SEND DATA TO THE SERIAL PORT: */
  7573.  
  7574.   /* Put the data we want to send to the serial port into */
  7575.   /* our own data buffer: (Well in this example we only   */
  7576.   /* send two bytes.)                                     */
  7577.   buffer[ 0 ] = 0x4C;
  7578.   buffer[ 1 ] = 0x31;
  7579.   
  7580.   /* We want to send (write) some data: */
  7581.   serial_req->IOSer.io_Command = CMD_WRITE;
  7582.  
  7583.   /* Give the start address of our data: */
  7584.   serial_req->IOSer.io_Data = (APTR) buffer;
  7585.  
  7586.   /* We want to send two bytes: */
  7587.   serial_req->IOSer.io_Length = 2;
  7588.  
  7589.   /* Do our request: */
  7590.   error = DoIO( serial_req );
  7591.   if( error )
  7592.     clean_up( "Could not send data to the serial port!" );
  7593.  
  7594.  
  7595.  
  7596.   /* READ DATA FROM THE SERIAL DEVICE: */
  7597.   
  7598.   /* We want to read some data: */
  7599.   serial_req->IOSer.io_Command = CMD_READ;
  7600.  
  7601.   /* Give the start address of our buffer: */
  7602.   serial_req->IOSer.io_Data = (APTR) buffer;
  7603.  
  7604.   /* We want to read 0 bytes: */
  7605.   /* (If you do not have anything connected to your serial */
  7606.   /* port, we better not wait for any signals, thus read   */
  7607.   /* only 0 bytes.)                                        */
  7608.   serial_req->IOSer.io_Length = 0;
  7609.  
  7610.   /* Do our request: */
  7611.   error = DoIO( serial_req );
  7612.   if( error )
  7613.     clean_up( "Could not read data from the serial port!" );
  7614.  
  7615.  
  7616.  
  7617.   /* THE END: */
  7618.   clean_up( "The End" );
  7619. }
  7620.  
  7621.  
  7622. /* Close and return everything that has been */
  7623. /* opened and allocated before we quit:      */
  7624. void clean_up( STRPTR text )
  7625. {
  7626.   /* Close the Parallel Device: */ 
  7627.   if( !serial_dever )
  7628.     CloseDevice( serial_req );
  7629.  
  7630.   /* Deallocate the serial request block: */
  7631.   if( serial_req )
  7632.     DeleteExtIO( serial_req, sizeof( struct IOExtSer ) );
  7633.  
  7634.   /* Remove the replyport: */
  7635.   if( replymp )
  7636.     DeletePort( replymp);
  7637.  
  7638.   /* Print the message: */
  7639.   printf( "\n%s\n", text );
  7640.  
  7641.   /* Quit: */
  7642.   exit( 0 );
  7643. }
  7644.  
  7645. 7.5  OTHER USEFUL COMMANDS
  7646.  
  7647. You can not only read and write from/to the serial port. There exist several 
  7648. other functions that can sometimes be needed. Here is a complete list:
  7649.  
  7650. 1. Break, stop the serial device for a short time.
  7651. 2. Clear, clear the input buffer.
  7652. 3. Flush, removes all queued requests.
  7653. 4. Query, get some information from the serial device.
  7654. 5. Reset, reinitializes the serial device.
  7655. 6. Start, restarts the serial communication.
  7656. 7. Stop, temporary stops the serial communication.
  7657.  
  7658. 7.5.1  BREAK
  7659.  
  7660. While you are sending or reading data you sometimes have to take a small pause. 
  7661. Your program may for example need to empty the data buffer, or coordinate its 
  7662. action with some other task. To pause the serial device you simply send a break 
  7663. request, and all communications is halted for a specified time.
  7664.  
  7665. You send a break signal by setting the io_Command field to "SDCMD_BREAK". The 
  7666. default is that all serial requests are immediately halted, but if the serial 
  7667. flag "SERF_QUEUEDBRK" is set, the break command will be queued as all other 
  7668. requests and the device will first take a break when all previous requests have 
  7669. been completed.
  7670.  
  7671. The serial device will normally take a 250000 microseconds (1/4 seconds) long 
  7672. break, but you may change this break time by altering the serial device's 
  7673. "io_BrkTime" parameter.
  7674.  
  7675. Here is an example on how you can send a break command:
  7676.  
  7677.   /* We want to take a pause: */
  7678.   ioreq->IOSer.io_Command = SDCMD_BREAK;
  7679.  
  7680.   /* Do our request: */
  7681.   error = DoIO( ioreq );
  7682.  
  7683.   /* OK? */
  7684.   if( error )
  7685.     printf( "Problems with the break!\n" );
  7686.  
  7687. 7.5.2  CLEAR
  7688.  
  7689. While you are reading data from the serial device, it is actually first stored 
  7690. in the internal input buffer, and then moved to your own buffer when requested. 
  7691. If you want to clear the buffer before you start to read you should set the 
  7692. io_Command field to "CMD_CLEAR". Here is an example:
  7693.  
  7694.   /* We want to clear the input buffer: */
  7695.   ioreq->IOSer.io_Command = CMD_CLEAR;
  7696.  
  7697.   /* Do our request: */
  7698.   error = DoIO( ioreq );
  7699.  
  7700.   /* OK? */
  7701.   if( error )
  7702.     printf( "Could not clear the input buffer!\n" );
  7703.  
  7704.  
  7705. 7.5.3  FLUSH
  7706.  
  7707. If several requests are sent to the device they are all queued on a FIFO (First 
  7708. In First Out) basis. The command "CMD_FLUSH" can then be used to remove all 
  7709. these queued commands. Here is an example:
  7710.  
  7711.   /* We want to remove all queued requests: */
  7712.   ioreq->IOSer.io_Command = CMD_FLUSH;
  7713.  
  7714.   /* Do our request: */
  7715.   error = DoIO( ioreq );
  7716.  
  7717.   /* OK? */
  7718.   if( error )
  7719.     printf( "Could not remove the queued requests!\n" );
  7720.  
  7721. 7.5.4  QUERY
  7722.  
  7723. The command "SDCMD_QUERY" can be used to get some information from the serial 
  7724. device. It is useful if you want to see the serial status and/or how many bytes 
  7725. there are already in the internal input buffer. The "io_Status" field of the 
  7726. request structure will be set as following table shows:
  7727.  
  7728. Bit
  7729.   
  7730. Hex
  7731.      
  7732. Active
  7733.   
  7734. Description
  7735.  
  7736.   0  0x0001  -       Reserved
  7737.   1  0x0002  -       Reserved
  7738.   2  0x0004  High    Connected to par. "select" and ser. "ring"
  7739.   3  0x0008  Low     DSR - Data Set Ready
  7740.   4  0x0010  Low     CTS - Clear To Send
  7741.   5  0x0020  Low     DCD - Carrier Detect
  7742.   6  0x0040  Low     RTS - Ready To Send
  7743.   7  0x0080  Low     DTR - Data Terminal Ready
  7744.   8  0x0100  High    Read overrun
  7745.   9  0x0200  High    Break sent
  7746.  10  0x0400  High    Break received
  7747.  11  0x0800  High    Transmit X-OFF
  7748.  12  0x1000  High    Receive X-OFF
  7749.  13  0x2000  -       Reserved
  7750.  14  0x4000  -       Reserved
  7751.  15  0x8000  -       Reserved
  7752.  
  7753. The field "io_Actual" of the request structure contains the number of bytes 
  7754. still left in the internal input buffer. Here is an example:
  7755.  
  7756.   /* Check the serial device: */
  7757.   ioreq->IOSer.io_Command = SDCMD_QUERY;
  7758.  
  7759.   /* Do our request: */
  7760.   error = DoIO( ioreq );
  7761.  
  7762.   /* OK? */
  7763.   if( error )
  7764.     printf( "Could not get any information from the device!\n" );
  7765.   else
  7766.   {
  7767.     /* Check the "io_Status" field: */
  7768.     if( ioreq->io_Status & 0x0200 )
  7769.       printf( "A break request have just been sent!\n" );    
  7770.  
  7771.     /* Check number of characters left in the input buffer: */
  7772.     printf( "Characters left: %ld\n", ioreq->IOSer.io_Actual );
  7773.   }
  7774.  
  7775. 7.5.5  RESET
  7776.  
  7777. Send the command "CMD_RESET" to reset the serial device. All commands that are 
  7778. queued to the device will be removed, commands that are currently executed will 
  7779. be aborted, the internal input buffer will be cleared and reallocated to the 
  7780. default size and finally all serial flags are resetted. Here is an example:
  7781.  
  7782.   /* We want to reset the serial device: */
  7783.   ioreq->IOSer.io_Command = CMD_RESET;
  7784.  
  7785.   /* Do our request: */
  7786.   error = DoIO( ioreq );
  7787.  
  7788.   /* OK? */
  7789.   if( error )
  7790.     printf( "Could not reset the serial device!\n" );
  7791.  
  7792. 7.5.6  START
  7793.  
  7794. After you have stopped the serial communication by sending an X-OFF message to 
  7795. the other device, you may want to start the communication again. It is done by 
  7796. sending an X-ON message, and the external device will then know that it may 
  7797. start to send/receive data. X-ON messages are sent to the other device (as well 
  7798. to our self) by issuing a "CMD_START" command. Here is an example:
  7799.  
  7800.   /* We want to start serial communication again: */
  7801.   ioreq->IOSer.io_Command = CMD_START;
  7802.  
  7803.   /* Do our request: */
  7804.   error = DoIO( ioreq );
  7805.  
  7806.   /* OK? */
  7807.   if( error )
  7808.     printf( "Could not start the serial communication!\n" );
  7809.  
  7810. 7.5.7  STOP
  7811.  
  7812. To temporary stop all serial communication you send a "CMD_STOP" command. If 
  7813. you try to stop the communication an X-OFF message will be sent to the external 
  7814. device (as well as to all other programs currently using the serial device). 
  7815. When a X-OFF message has been received, communication will first start again 
  7816. when an X-ON message is sent. (See CMD_START.) Here is an example:
  7817.  
  7818.   /* We want to temporary stop all serial communication: */
  7819.   ioreq->IOSer.io_Command = CMD_STOP;
  7820.  
  7821.   /* Do our request: */
  7822.   error = DoIO( ioreq );
  7823.  
  7824.   /* OK? */
  7825.   if( error )
  7826.     printf( "Could not stop the serial communication!\n" );
  7827.  
  7828. 7.6  FUNCTIONS
  7829.  
  7830. DoIO()
  7831.  
  7832. DoIO() is used to send requests to a device, and waits for it to be completed. 
  7833. While the program is waiting it is put to sleep so it will not waste any 
  7834. computer time. DoIO() will return first when the request have been completed or 
  7835. failed, and no message is therefore sent to the reply port.
  7836.  
  7837. Synopsis:
  7838.     
  7839. error = DoIO( req );
  7840.  
  7841. error:
  7842.     
  7843. (long) DoIO() will return first when the request has been completed or 
  7844. something has failed. If the request was successfully completed zero is 
  7845. returned, else an error number is returned. What error number depends on which 
  7846. device was used.
  7847.  
  7848. req:
  7849.     
  7850. (struct IORequest *) Pointer to the request you want to have executed.
  7851.  
  7852. SendIO()
  7853.  
  7854. SendIO() is used to send requests to a device, but will return immediately 
  7855. without any delay. To check if the request have been completed use the 
  7856. CheckIO() function, or look at the request's reply port for any messages. Once 
  7857. the request has been completed you must remove the message at the reply port. 
  7858. (CheckIO() will not do it.) To remove a message use the function Remove(). Note 
  7859. that you may NOT close the serial device before all requests have been 
  7860. completed or aborted!
  7861.  
  7862. Synopsis:
  7863.     
  7864. SendIO( req )
  7865.  
  7866. req:
  7867.     
  7868. (struct IORequest *) Pointer to the request you want to have executed.
  7869.  
  7870. CheckIO()
  7871.  
  7872. CheckIO() is used to check if a previously started request has been completed. 
  7873. Note that this function will not remove the message at the reply port. This 
  7874. must be done with the Remove() function.
  7875.  
  7876. Synopsis:
  7877.     
  7878. ptr = CheckIO( req );
  7879.  
  7880. ptr:
  7881.     
  7882. (long) CheckIO() will either return NULL if the request have not been completed 
  7883. or it will return a pointer to the request block.
  7884.  
  7885. req:
  7886.     
  7887. (struct IORequest *) Pointer to the request you want to check.
  7888.  
  7889. WaitIO()
  7890.  
  7891. WaitIO() will wait for the request to be completed, and while the program is 
  7892. waiting it is put to sleep so no computer time is wasted.
  7893.  
  7894.  
  7895. Synopsis:
  7896.     
  7897. error = WaitIO( req );
  7898.  
  7899. error:
  7900.     
  7901. (long) WaitIO() will return first when the request, that has previously been 
  7902. sent, has been completed or something has failed. If the request was 
  7903. successfully completed zero is returned, else an error number is returned. What 
  7904. error number depends on which device was used.
  7905.  
  7906. req:
  7907.     
  7908. (struct IORequest *) Pointer to the request you want to wait for to be 
  7909. completed. Note that the request must have already been sent to the device by 
  7910. either a SendIO() or BeginIO() function call.
  7911.  
  7912. BeginIO()
  7913.  
  7914. BeginIO() is a low level form of the SendIO() function. The advantage with 
  7915. BeginIO() is that no fields of the request block will be altered as which is 
  7916. the case with SendIO(). If you are for example using the "quick mode" you 
  7917. should not use SendIO() since it will alter some of the data blocks.
  7918.  
  7919. BeginIO() is synchronous command if you use the quick mode, but if you are not, 
  7920. the command will be asynchronous. Note that you may NOT close the serial device 
  7921. before all requests have been completed or aborted, so be careful with 
  7922. asynchronous commands.
  7923.  
  7924. Synopsis:
  7925.     
  7926. BeginIO( req )
  7927.  
  7928. req:
  7929.     
  7930. (struct IORequest *) Pointer to the request you want to have executed. 
  7931. BeginIO() will not alter any values in this request structure as which is the 
  7932. case with SendIO() and DoIO(). Normally this is not any problem, and thus the 
  7933. SendIO() and DoIO() functions should be used. However, with the Audio Device 
  7934. for example some of these fields which are altered should not be changed, and 
  7935. you should therefore use this low level function BeginIO().  
  7936.  
  7937. AbortIO()
  7938.  
  7939. AbortIO() will try to abort a previously started request. This function should 
  7940. be used sparsely since it does not look so good if you start a request and the 
  7941. try to stop it. (Better not start it at all.) However, it is easy, and can 
  7942. sometimes be very useful.
  7943.  
  7944. A request that is aborted will have its io_Error field set to IOERR_ABORTED 
  7945. (defined in header file "exec/errors.h").
  7946.  
  7947. Synopsis:
  7948.     
  7949. AbortIO( req )
  7950.  
  7951. req:
  7952.     
  7953. (struct IORequest *) Pointer to the request you want to abort.
  7954.  
  7955. CloseDevice()
  7956.  
  7957. CloseDevice() will (surprise!) close a device. If you close the serial device 
  7958. the internal input buffer will automatically be deallocated. Note that you 
  7959. should NOT close the device before all started asynchronous requests have been 
  7960. either completed or aborted.
  7961.  
  7962. Synopsis:
  7963.     
  7964. CloseDevice( ioreq );
  7965.  
  7966. ioreg:
  7967.     
  7968. (struct IORequest *) Pointer to the device's request block.
  7969.  
  7970.  
  7971. OpenDevice()
  7972.  
  7973. OpenDevice() will try to open the specified device.
  7974.   
  7975. Synopsis:
  7976.     
  7977. error = OpenDevice( name, unit, req, flags );
  7978.  
  7979. error:
  7980.     
  7981. (long) If OpenDevice() managed to open the device it returns 0, else an error 
  7982. number is returned. If you try to open the serial device, and there is already 
  7983. a program that is using it, the error message "SerErr_DevBusy" is returned.
  7984.  
  7985. name:
  7986.     
  7987. (char *) Name of the device you want to open. The name of the serial device is 
  7988. defined as SERIALNAME in header file "devices/serial.h".
  7989.  
  7990. unit:
  7991.     
  7992. (long) Which unit you want to open. Since there exist only one serial port, 
  7993. this field is ignored.
  7994.  
  7995. req:
  7996.     
  7997. (struct IORequest *) Pointer to a request block. For the serial device it must 
  7998. be a pointer to an extender serial request block (struct IOExtSer).
  7999.  
  8000. flags:
  8001.     
  8002. (long) Any special mode is set here. Ignored by the serial device.
  8003.  
  8004. 7.7  COMMANDS
  8005.  
  8006. Here is a complete list of commands you may send to the serial device. For full 
  8007. documentation se examples above.
  8008.  
  8009. The special serial device commands: (Defined in header file "devices/serial.h")
  8010.  
  8011. SDCMD_BREAK
  8012.     
  8013. Sends a break signal.
  8014. SDCMD_QUERY
  8015.     
  8016. Check the status of the serial device.
  8017. SDCMD_SETPARAMS
  8018.     
  8019. Set the parameters of the serial device.
  8020.  
  8021. The rest of the commands you may use are normal exec commands, and are defined 
  8022. in header file "exec/io.h".
  8023.  
  8024. CMD_RESET
  8025.     
  8026. Resets all parameters of the serial device.
  8027. CMD_READ
  8028.     
  8029. Read data from the serial port.
  8030. CMD_WRITE
  8031.     
  8032. Write data to the serial port.
  8033. CMD_CLEAR
  8034.     
  8035. Clears the internal input buffer.
  8036. CMD_STOP
  8037.     
  8038. Temporary stops all serial communication. (X-OFF)
  8039. CMD_START
  8040.     
  8041. Restarts serial communication. (X-ON)
  8042. CMD_FLUSH
  8043.     
  8044. Removes all queued requests.
  8045.  
  8046. 7.8  EXAMPLES
  8047.  
  8048. The included examples demonstrates how you can use the Serial Device. It 
  8049. demonstrates most of the features described in this chapter. Since I do not 
  8050. know what you have connected to your serial port the examples will not do very 
  8051. much. However, the examples are easy modify, so it should not be hard for you 
  8052. to change them as desired.
  8053.  
  8054. Example 1
  8055. If you have a Sharp JX-100 scanner you can run this program since it will try 
  8056. to turn the lamp on and then off again. Very useful! (hmmm...) The program does 
  8057. not check if there is any contact with the scanner, nor if the lamp really was 
  8058. turned on or not. It simply demonstrates how to send data.
  8059.  
  8060. Example 2
  8061. This example is rather similar to Example 1, but this time we do not wait for 
  8062. the serial port to complete our request. Instead we do somethings (well not 
  8063. very much) and now and then checks if the request has been completed. Using a 
  8064. busy wait.
  8065.  
  8066. Example 3
  8067. This example is also rather similar to Example 1, but this time we try to read 
  8068. and write at the same time. To be able to do several requests simultaneously we 
  8069. need one request block for each command. In this example we use three separate 
  8070. request blocks. Using asynchronous commands but puts the task to sleep just 
  8071. before we clear and return  everything.
  8072.  
  8073. Example 4
  8074. This example does not do anything, but it consists of several useful functions 
  8075. that you can use yourself after small modifications. The functions demonstrates 
  8076. all commands there exist for the serial device, so if you had problems in 
  8077. understanding how a command was used you can look here.
  8078.  
  8079.  
  8080. PARALLEL DEVICE
  8081.  
  8082. 8.1  INTRODUCTION
  8083.  
  8084. All Amiga models have a parallel port to which you can connect external devices 
  8085. like a printer, a video digitizer or a sound sampler. The most common external 
  8086. device for the parallel port is undoubtedly a printer, although some printers 
  8087. are connected to the serial device.
  8088.  
  8089. The parallel port can send and receive eight bits simultaneously. This can be 
  8090. compared with the serial port which only can send/receive a stream of bits. The 
  8091. parallel port is because of this much faster and is therefore often used for 
  8092. video digitizers or sound samplers.
  8093.  
  8094. The parallel device helps you to work with the printer at a very low level, but 
  8095. is still easy to handle. The parallel device can as the serial device be locked 
  8096. for exclusive access or you can allow other programs to use the device 
  8097. simultaneously.
  8098.  
  8099. It is important to note that the parallel device should only be used when you 
  8100. want to handle the port directly at a low level. This can be useful when you 
  8101. want to collect data from a video digitizer, or send untranslated printer 
  8102. codes. However, if you simply want to use the printer you should use the 
  8103. printer device instead. The printer device will automatically take care of all 
  8104. printer handling, and is using the preference settings. See next chapter (29) 
  8105. "Printer Device".
  8106.  
  8107. 8.2  PARALLEL PORT
  8108.  
  8109. A parallel port sends a whole byte each time as explained above, and is 
  8110. therefore very fast. Data that is sent to or received from the parallel port 
  8111. does not need to be translated in any way, it is immediately usable.
  8112.  
  8113. The Amiga's parallel (Centronics) port is a 25-pin D-female- type connector. 
  8114. (On the old A1000s the parallel port have a male connector.) Below is an almost 
  8115. complete list of the pin assignment, together with a short description. (See 
  8116. illustration "Centronics".)
  8117.  
  8118.   Pin  Name    Direction  Description
  8119.   -----------------------------------------------------
  8120.     1  STROBE  Out        Used to coordinate the events
  8121.     2  Data 0  In/Out     Bit 0
  8122.     3  Data 1  In/Out     Bit 1
  8123.     4  Data 2  In/Out     Bit 2
  8124.     5  Data 3  In/Out     Bit 3
  8125.     6  Data 4  In/Out     Bit 4
  8126.     7  Data 5  In/Out     Bit 5
  8127.     8  Data 6  In/Out     Bit 6
  8128.     9  Data 7  In/Out     Bit 7
  8129.    10  ACK     In         Data acknowledge
  8130.    11  BUSY    In/Out     General Input/Output pin
  8131.    12  POUT    In/Out     General Input/Output pin
  8132.    13  SEL     In/Out     General Input/Output pin
  8133.    14  +5V     -          +5 Volt
  8134.    15  NC      In/Out     No connection pin
  8135.    16  RESET   Out        The system resets
  8136.    17  GND     -          Signal ground
  8137.    18  GND     -          Signal ground
  8138.    19  GND     -          Signal ground
  8139.    20  GND     -          Signal ground
  8140.    21  GND     -          Signal ground
  8141.    22  GND     -          Signal ground
  8142.    23  GND     -          Signal ground
  8143.    24  GND     -          Signal ground
  8144.    25  GND     -          Signal ground
  8145.  
  8146. 8.3  PARALLEL DEVICE
  8147.  
  8148. The parallel device is very similar to the serial device. It can either be used 
  8149. in exclusive mode, or several programs may use the port simultaneously, shared 
  8150. access. When you want that the parallel device to do something you simply send 
  8151. an already initialized request block (struct IOExtPar), and the device will 
  8152. send a message to the reply port when the request has been done. Exactly as all 
  8153. other devices.
  8154.  
  8155. 8.3.1  THE PARALLEL REQUESTBLOCK
  8156.  
  8157. The request block you should use with the parallel device look like this: 
  8158. (defined in header file "devices/parallel.h")
  8159.  
  8160. struct IOExtPar
  8161. {
  8162.   struct IOStdReq IOPar;
  8163.   ULONG io_PExtFlags;
  8164.   UBYTE io_Status;
  8165.   UBYTE io_ParFlags;
  8166.   struct IOPArray io_PTermArray;
  8167. };
  8168.  
  8169. IOPar:
  8170.     
  8171. This is the standard request block. The IOStdReq structure is defined in header 
  8172. file "exec/io.h", and is fully documented in chapter 17 "Devices". 
  8173.  
  8174. io_PExtFlags:
  8175.     
  8176. This is currently not used, but will maybe be used in the future when more 
  8177. parallel flags are needed. 
  8178.  
  8179. io_Status:
  8180.     
  8181. The status of the parallel port and parallel device. There exist for the moment 
  8182. four status flags:
  8183.                
  8184.     
  8185. IOPTF_RWDIR    If this flag is set the device is currently writing to the 
  8186. parallel device. On the other hand, if the flag is not set the device is 
  8187. collecting data at the parallel port. 
  8188.  
  8189.     
  8190. IOPTF_PARSEL   Printer selected.
  8191.  
  8192.     
  8193. IOPTF_PAPEROUT The printer ran out of paper. Inform the user!
  8194.  
  8195.     
  8196. IOPTF_PARBUSY  The parallel port is currently busy.
  8197.                                 
  8198.     
  8199. Use the command "PDCMD_QUERY" before you look at these fields to make sure 
  8200. everything is up to date. 
  8201.  
  8202. io_ParFlags:
  8203.     
  8204. This field contains all special parallel flags. There exist only three flags 
  8205. for the moment, and one of these is still not usable. Here is the complete 
  8206. list:
  8207.     
  8208. PARF_SHARED     Set this flag if you want to share the parallel port with other 
  8209. programs. Note that this flag should only be altered before you have opened the 
  8210. parallel device, and should NOT be changed later on.
  8211.                                 
  8212.     
  8213. If you want to change status you should close the parallel device, alter the 
  8214. status and then try to open the device again.
  8215.  
  8216.     
  8217. PARF_RAD_BOOGIE This flag is currently not used. It is supposed to be set when 
  8218. you want to send/receive data at a very high speed.
  8219.  
  8220.     
  8221. PARF_EOFMODE    This is actually the only flag you may alter after you have 
  8222. opened the device. If the flag is set the parallel device will immediately stop 
  8223. the transmission of data when it finds one of the specified end-of-file 
  8224. characters.
  8225.  
  8226. io_PTermArray:
  8227.     
  8228. This field contains eight characters which will be treated as the end-of-file 
  8229. characters if the PARF_EOFMODE flag is set.
  8230.  
  8231. 8.3.2  OPEN THE PARALLEL DEVICE
  8232.  
  8233. As with all devices you have to open a message port through which the parallel 
  8234. device can communicate with you, and allocate a request block (a IOExtPar 
  8235. structure), before you may open the device itself.
  8236.  
  8237. 1. Open a message port: (Since it is only our task and the device that will use 
  8238. the message port, we do not need to make it "public", hence no name. Priority 
  8239. should as usual be set to 0, normal priority.)
  8240.  
  8241.      struct MsgPort *replymp;
  8242.  
  8243.      replymp = (struct MsgPort *)
  8244.        CreatePort( NULL, 0 );
  8245.  
  8246.      if( !replymp )
  8247.        clean_up( "Could not create the reply port!" );
  8248.  
  8249. 2. Allocate a request block of type IOExtPar structure. (The IOExtPar structure 
  8250. is an extended version of the normal request block, and should therefore be 
  8251. allocated with help of the CreateExtIO() function with the size set to sizeof( 
  8252. struct IOExtPar ).
  8253.  
  8254.      struct IOExtPar *parallel_req;
  8255.  
  8256.      parallel_req = (struct IOExtPar *)
  8257.        CreateExtIO( replymp, sizeof( struct IOExtPar ) );
  8258.  
  8259.      if( !parallel_req )
  8260.        clean_up( "Not enough memory!" ); 
  8261.  
  8262. Once the message port and the request block have successfully been created, you 
  8263. need to decide if you want exclusive or shared access. If you want shared 
  8264. access, other programs may also use the parallel port, should you set the flag 
  8265. "PARF_SHARED" in the io_ParFlags" field. If you want exclusive access you do 
  8266. not need to set any flags, since it is the default mode.
  8267.  
  8268. 3. Either set shared or exclusive access, and open the device.
  8269.  
  8270.      UBYTE error;
  8271.  
  8272.      /* We want shared access: */
  8273.      parallel_req->io_ParFlags = PARF_SHARED;
  8274.  
  8275.      /* Open the parallel device: */
  8276.      error = OpenDevice( PARALLELNAME, 0, parallel_req, 0 );
  8277.  
  8278.      if( error )
  8279.        clean_up( "Could not open the Parallel Device!" );
  8280.  
  8281. 8.3.3  SET PARALLEL PARAMETERS
  8282.  
  8283. Once you have successfully opened the parallel device you may set some 
  8284. parameters that tells the device how it should work. Luckily, the parallel 
  8285. device is much simpler than the serial device.
  8286.  
  8287. For the moment there exist only one parallel flag you may use, and that is 
  8288. PARF_EOFMODE. It should be set if you want the device to immediately stop the 
  8289. current communication if one of the eight specified end-of-file characters 
  8290. appears. To set the flag do like this:
  8291.  
  8292.   /* Look for end-of-file characters: */
  8293.   parallel_req->io_ParFlags += PARF_EOFMODE;
  8294.  
  8295. Note that we used the sign "+=". This is very useful if you want to set one 
  8296. flag (bit), but keep all others unchanged. If we simply used the sign "=" the 
  8297. "PARF_SHARED" flag we previously had set would be erased. We could of course 
  8298. have written it like this:
  8299.  
  8300.   /* Set both the end-of-file and shared mode: */
  8301.   parallel_req->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
  8302.  
  8303. This is however not equally good, since it is easy to forget one flag, and then 
  8304. it would be erased. So if you want to add flags use the command "+=". To erase 
  8305. one flag you only have to do the opposite and subtract it ("-=").
  8306.  
  8307. If you want to use the end-of-file mode, and have set the "PARF_EOFMODE" flag, 
  8308. you must also tell the device which characters should be treated as end-of-file 
  8309. characters. The list of these characters is stored in the "IOPArra" array which 
  8310. is placed at the bottom of the request structure, and looks like this:
  8311.  
  8312.   struct IOPArray
  8313.   {
  8314.     ULONG PTermArray0;
  8315.     ULONG PTermArray1;
  8316.   };
  8317.  
  8318. Each ULONG data consists of four bytes, and in total you can store eight bytes 
  8319. (end-of-file characters) in the array. To make the checking routine efficient 
  8320. you must store the characters in descending order! To copy the desired 
  8321. characters into the array do like this:
  8322.  
  8323.   /* Here is an array with all EOF characters: */
  8324.   /* NOTE! They MUST be in descending order!   */
  8325.   UBYTE eof_char[8]={ 0x54, 0x32, 0x16, 0x15, 0x12, 0x03, 0x00, 0x00 };
  8326.  
  8327.   /* Declare a unsigned byte pointer: */
  8328.   UBYTE *ptr;
  8329.  
  8330.   /* Simple loop variable: */
  8331.   int loop;
  8332.  
  8333.   ...
  8334.  
  8335.   /* Get the address of the IOTArray: */
  8336.   ptr = (UBYTE *) &(ioreq->io_PTermArray);
  8337.  
  8338.   /* Set all eight end of file characters: */
  8339.   for( loop=0; loop < 8; loop++ )
  8340.   {
  8341.     /* Copy character after character: */
  8342.     *ptr = eof_chars[ loop ];
  8343.  
  8344.     /* Step one byte forward: */
  8345.     ptr++;
  8346.   }
  8347.  
  8348. Once you have set all desired parameters in the request block (IOExtPar 
  8349. structure) you set the IO command to PDCMD_SETPARAMS and tell the parallel 
  8350. device to do your request by calling the DoIO() function. If something went 
  8351. wrong DoIO() will return with an error number, else 0 is returned which means 
  8352. that your request have successfully been executed. (See below for a complete 
  8353. list of error messages.) Here is an example:
  8354.  
  8355.   /* We want to set the parallel device's parameters: */
  8356.   ioreq->IOPar.io_Command = PDCMD_SETPARAMS;
  8357.  
  8358.   /* Do our request, and return when done: */
  8359.   error = DoIO( ioreq );
  8360.  
  8361. 8.3.4  WRITE DATA
  8362.  
  8363. To send data to the parallel device you have to:
  8364.  
  8365. 1. Set the command flag "CMD_WRITE" in the "io_Command" field.
  8366.  
  8367. 2. Tell the parallel device how many bytes you want to send by setting the 
  8368. "io_Length" field as desired. If you set it to -1 the device will continuously 
  8369. send data and stop first when an end-of-file character is found. (The 
  8370. end-of-file character will also be sent.) Note that the parallel flag 
  8371. "PARF_EOFMODE" must have been set, or the device will never stop sending data.
  8372.  
  8373. 3. Give the field "io_Data" the address of the first byte of data that should 
  8374. be sent.
  8375.  
  8376. 4. Finally you send the request to the parallel device. If you want to wait for 
  8377. the request to be completed (synchronous) you should use the DoIO() function. 
  8378. On the other hand, if you want that your program continues to work while your 
  8379. request is completed, you should use the SendIO() function.
  8380.  
  8381. Here is an example on how to write data to the parallel port: (In this example 
  8382. the program will be put to sleep while the data is sent to the parallel port.)
  8383.  
  8384.  
  8385. error:
  8386.     
  8387. (UBYTE) is a simple unsigned byte variable.
  8388.  
  8389. ioreq:
  8390.     
  8391. (struct IOExtPar *) is a pointer to an IOExtPar structure.
  8392.  
  8393. data:
  8394.     
  8395. (BYTE) is a pointer to a block of memory where all data you want to send is 
  8396. located.
  8397.  
  8398.   /* We want to send (write) some data: */
  8399.   ioreq->IOPar.io_Command = CMD_WRITE;
  8400.  
  8401.   /* Give the start address of our data buffer: */
  8402.   ioreq->IOPar.io_Data = data;
  8403.  
  8404.   /* We want to send 150 bytes/characters: */
  8405.   ioreq->IOPar.io_Length = 150;
  8406.  
  8407.   /* Do our request: */
  8408.   error = DoIO( ioreq );
  8409.  
  8410.   /* Everything OK? */
  8411.   if( error )
  8412.     printf( "Problems while writing!\n" );
  8413.  
  8414. If you do not want to wait for the request to be completed you should use the 
  8415. SendIO() function instead. To check later if therequest have been completed or 
  8416. not you can either use the function CheckIO(), or wait for a message to arrive 
  8417. at the request block's reply port. (Since other request may also send messages 
  8418. to this port, it is usually easiest to use CheckIO().)
  8419.  
  8420. Once the request have been completed you can look at the io_Error field of the 
  8421. request block to check if everything was OK. If the field is zero everything 
  8422. was executed without any problems, but if it is non zero the request failed.
  8423.  
  8424. Here is an example:
  8425.  
  8426.   /* Declare a pointer and set it to NULL: */
  8427.   struct IOExtPar *ptr = NULL;
  8428.  
  8429.   ...
  8430.   
  8431.  
  8432.   /* We want to send (write) some data: */
  8433.   ioreq->IOPar.io_Command = CMD_WRITE;
  8434.  
  8435.   /* Give the start address of our data buffer: */
  8436.   ioreq->IOPar.io_Data = data;
  8437.  
  8438.   /* We want to send 280 bytes/characters: */
  8439.   ioreq->IOPar.io_Length = 280;
  8440.  
  8441.   /* Do our request and return immediately: */
  8442.   SendIO( ioreq );
  8443.  
  8444.  
  8445.   /* As long as the pointer is not pointing to */
  8446.   /* the request we should stay in the loop:   */
  8447.   while( ptr == NULL )
  8448.   {
  8449.     ... do something ...
  8450.  
  8451.   
  8452.     /* Check if the request has been completed: (If the  */
  8453.     /* request has been completed CheckIO() will return  */
  8454.     /* a pointer to the request, else NULL is returned.) */
  8455.     ptr = CheckIO( ioreq );
  8456.   }
  8457.  
  8458.   /* Remove the requst block's message. (The ptr and ioreq */
  8459.   /* are in this example identical, so it does not matter  */
  8460.   /* whichever you will use. The parenthesis around the    */
  8461.   /* expression is actually unnecessary, but this looks    */
  8462.   /* better.)                                              */
  8463.   Remove( &(ptr->IOPar.io_Message.mn_Node) );
  8464.  
  8465.   /* Everything OK? Check the io_Error filed: */
  8466.   if( ioreq->IOPar.io_Error )
  8467.     printf( "Problems while writing!\n" );
  8468.  
  8469. 8.3.5  READ DATA
  8470.  
  8471. Although most people use the parallel port to send data to their printer, more 
  8472. and more users connect other external devices like video digitzers and sound 
  8473. samplers. With these devices you do not only need to send data, you also have 
  8474. to collect data from the parallel port.
  8475.  
  8476. The process of collecting (reading) is very similar to reading. Here is what 
  8477. you have to do:
  8478.  
  8479. 1. Set the command flag "CMD_READ" in the "io_Command" field.
  8480.  
  8481. 2. Tell the parallel device how many bytes you want to read by setting the 
  8482. "io_Length" field as desired. If you set it to -1 the device will continuously 
  8483. read data and stop first when an end-of-file character is received. (The 
  8484. end-of-file character will also be collected.) Note that the parallel flag 
  8485. "PARF_EOFMODE" must have been set, or the device will never stop collecting 
  8486. data.
  8487.  
  8488. 3. Give the field "io_Data" a pointer to your data buffer where all collected 
  8489. data should be placed. Note that the buffer must be big enough so all data will 
  8490. fit! 
  8491.  
  8492. 4. Finally you send the request to the parallel device. If you want to wait for 
  8493. the request to be completed (synchronous) you should as usual use the DoIO() 
  8494. function. On the other hand, if you want that your program continues to work 
  8495. while your request is completed, you should use the SendIO() function.
  8496.  
  8497. Here is an example on how to read data: (Your program will be put to sleep 
  8498. while the data is collected.)
  8499.  
  8500. error:
  8501.     
  8502. (UBYTE) is a simple unsigned byte variable.
  8503.  
  8504. ioreq:
  8505.     
  8506. (struct IOExtPar *) is a pointer to an IOExtPar structure.
  8507.  
  8508. data:
  8509.     
  8510. (BYTE) is a pointer to a block of memory where all data which is collected will 
  8511. be stored. Note that the data buffer must be big enough so all data will fit.
  8512.   /* We want to read some data: */
  8513.   ioreq->IOPar.io_Command = CMD_READ;
  8514.  
  8515.   /* Give the start address of our data buffer: */
  8516.   ioreq->IOPar.io_Data = data;
  8517.  
  8518.   /* We want to read 400 bytes/characters: (The buffer must */
  8519.   /* then be at least 400 bytes.)                           */
  8520.   ioreq->IOPar.io_Length = 400;
  8521.  
  8522.   /* Do our request: */
  8523.   error = DoIO( ioreq );
  8524.  
  8525.   /* Everything OK? */
  8526.   if( error )
  8527.     printf( "Problems while reading!\n" );
  8528.  
  8529. The program above will go to sleep while the data is collected from the 
  8530. parallel port. If you want to do something while the data is fetched and not go 
  8531. to sleep you should use the asynchronous function SendIO() function as 
  8532. explained above. Here is an example:
  8533.  
  8534.   /* Declare a pointer and set it to NULL: */
  8535.   struct IOExtPar *ptr = NULL;
  8536.  
  8537.   ...
  8538.   
  8539.  
  8540.   /* We want to read some data: */
  8541.   ioreq->IOPar.io_Command = CMD_READ;
  8542.  
  8543.   /* Give the start address of our data buffer: */
  8544.   ioreq->IOPar.io_Data = data;
  8545.  
  8546.   /* We want to read 400 bytes/characters: (The buffer must */
  8547.   /* then be at least 400 bytes.)                           */
  8548.   ioreq->IOPar.io_Length = 400;
  8549.  
  8550.   /* Do our request and return immediately: */
  8551.   SendIO( ioreq );
  8552.  
  8553.  
  8554.   /* As long as the pointer is not pointing to */
  8555.   /* the request we should stay in the loop:   */
  8556.   while( ptr == NULL )
  8557.   {
  8558.     ... do something ...
  8559.  
  8560.   
  8561.     /* Check if the request has been completed: (If the  */
  8562.     /* request has been completed CheckIO() will return  */
  8563.     /* a pointer to the request, else NULL is returned.) */
  8564.     ptr = CheckIO( ioreq );
  8565.   }
  8566.  
  8567.   /* Remove the requst block's message. (The ptr and ioreq */
  8568.   /* are in this example identical, so it does not matter  */
  8569.   /* whichever you will use. The parenthesis around the    */
  8570.   /* expression is actually unnecessary, but this looks    */
  8571.   /* better.)                                              */
  8572.   Remove( &(ptr->IOPar.io_Message.mn_Node) );
  8573.  
  8574.   /* Everything OK? Check the io_Error filed: */
  8575.   if( ioreq->IOPar.io_Error )
  8576.     printf( "Problems while reading!\n" );
  8577.  
  8578. 8.3.6  HOW TO HANDLE SEVERAL REQUESTS SIMULTANIOUSLY
  8579.  
  8580. Since you usually will want to read and write data at the same time, you have 
  8581. to use two separate request blocks. If you are using shared access mode you 
  8582. simply create two request blocks and open the parallel device twice. (Do not 
  8583. forget to close both requests later on.) Here is an example:
  8584.  
  8585.   struct MsgPort *replymp;
  8586.   struct IOExtPar *parallel_req_read;
  8587.   struct IOExtPar *parallel_req_write;
  8588.   UBYTE error;
  8589.  
  8590.      
  8591.   /* We use only one reply message port: */
  8592.   replymp = (struct MsgPort *)
  8593.     CreatePort( NULL, 0 );
  8594.   if( !replymp )
  8595.     clean_up( "Could not create the reply port!" );
  8596.  
  8597.   /* Create the request block "read": */
  8598.   parallel_req_read = (struct IOExtPar *)
  8599.     CreateExtIO( replymp, sizeof( struct IOExtPar ) );
  8600.   if( !parallel_req_read )
  8601.     clean_up( "Not enough memory!" );
  8602.   parallel_req_read->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
  8603.  
  8604.   /* Create the request block "write": */
  8605.   parallel_req_write = (struct IOExtPar *)
  8606.     CreateExtIO( replymp, sizeof( struct IOExtPar ) );
  8607.   if( !parallel_req_write )
  8608.     clean_up( "Not enough memory!" );
  8609.   parallel_req_write->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
  8610.  
  8611.  
  8612.   /* Open the parallel device for the read request: */
  8613.   error = OpenDevice( PARALLELNAME, 0, parallel_req_read, 0 );
  8614.   if( error )
  8615.     clean_up( "Could not open the parallel device (Read)!" );
  8616.  
  8617.   /* Open the parallel device for the write request: */
  8618.   error = OpenDevice( PARALLELNAME, 0, parallel_req_write, 0 );
  8619.   if( error )
  8620.     clean_up( "Could not open the parallel device (Write)!" );
  8621. If you are using exclusive access mode you can of course not open the parallel 
  8622. device twice. Instead we have to copy the first request block to the other 
  8623. request block, byte for byte. Here is an example:
  8624.  
  8625.   struct MsgPort *replymp;
  8626.   struct IOExtPar *parallel_req_read;
  8627.   struct IOExtPar *parallel_req_write;
  8628.   BYTE *r_ptr;
  8629.   BYTE *w_ptr;
  8630.   UBYTE error;
  8631.   int loop;
  8632.  
  8633.      
  8634.   /* We use only one reply message port: */
  8635.   replymp = (struct MsgPort *)
  8636.     CreatePort( NULL, 0 );
  8637.   if( !replymp )
  8638.     clean_up( "Could not create the reply port!" );
  8639.  
  8640.  
  8641.   /* Create the request block "read": */
  8642.   parallel_req_read = (struct IOExtPar *)
  8643.     CreateExtIO( replymp, sizeof( struct IOExtPar ) );
  8644.   if( !parallel_req_read )
  8645.     clean_up( "Not enough memory!" );
  8646.   parallel_req_read->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
  8647.  
  8648.   /* Create the request block "write": */
  8649.   parallel_req_write = (struct IOExtPar *)
  8650.     CreateExtIO( replymp, sizeof( struct IOExtPar ) );
  8651.   if( !parallel_req_write )
  8652.     clean_up( "Not enough memory!" );
  8653.   parallel_req_write->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
  8654.  
  8655.  
  8656.   /* Open the parallel device for the read request: */
  8657.   error = OpenDevice( PARALLELNAME, 0, parallel_req_read, 0 );
  8658.   if( error )
  8659.     clean_up( "Could not open the Parallel Device (Read)!" );
  8660.  
  8661.  
  8662.   /* Since we can not open the parallel device once again */
  8663.   /* for the write request, we have to copy the whole     */
  8664.   /* read request block into the write request block.     */
  8665.  
  8666.   /* Get the start address of both request blocks: */
  8667.   r_ptr = (BYTE *) parallel_req_read;
  8668.   w_ptr = (BYTE *) parallel_req_write;
  8669.  
  8670.   /* Copy the request block, byte by byte: */
  8671.   for( loop=0; loop < sizeof( struct IOExtPar ); loop++ )
  8672.   {
  8673.     /* Copy one byte: */
  8674.     *w_ptr = *r_ptr;
  8675.     
  8676.     /* Step one byte forward: */
  8677.     w_ptr++;
  8678.     r_ptr++;
  8679.   }
  8680.  
  8681. Now we have two request blocks, and can therefore use both read and write 
  8682. requests at the same time. If you also want to use the special extra functions 
  8683. described further down, you may want to create even one more request block (or 
  8684. maybe even more...). The procedure is the same as described above.
  8685.  
  8686. 8.3.7  ERRORS
  8687.  
  8688. While you are using the parallel device you may sometimes encounter an error 
  8689. message. Usually it is easy to guess what went wrong, but it is always good to 
  8690. check what really happened. There exit for the moment seven different types of 
  8691. parallel port errors, all defined in the header file "devices/parallel.h".
  8692.  
  8693. You will either receive the error message from the function you just called 
  8694. (for example, DoIO() returns 0 or an error number), or you can check the 
  8695. request block to see if there were any problems. (The io_Error filed of the 
  8696. request block either contains 0, which means everything is OK, or an error 
  8697. number.)
  8698.  
  8699. Here is a complete list of the parallel error messages:
  8700.  
  8701. ParErr_DevBusy:
  8702.     
  8703. Some other task/request is already using the parallel device.
  8704.  
  8705. ParErr_BufTooBig:
  8706.     
  8707. Buffer too big. Hey, what is this?
  8708.  
  8709. ParErr_InvParam:
  8710.     
  8711. The request block's parameters were not properly initialized.
  8712.  
  8713. ParErr_LineErr:
  8714.     
  8715. There were some problems with the communication. The parallel cable is faulty 
  8716. or the other device is not properly initialized or not connected. Tell the user 
  8717. to check the cables!
  8718.  
  8719. ParErr_NotOpen:
  8720.     
  8721. The parallel device was not open!
  8722.  
  8723. ParErr_PortReset:
  8724.     
  8725. The parallel device have just been resetted. Someone just sent a CMD_Reset 
  8726. request. Default parameters are set.
  8727.  
  8728. ParErr_InitErr:
  8729.     
  8730. The parallel device could not be initialized with your requirements. (Probably 
  8731. forgot to clear all unused flags.)
  8732.  
  8733. While you are using the parallel device it may happen that you also receive 
  8734. error messages from Exec. (Exec is handling all stuff like messages, requests, 
  8735. tasks and so on.) Here is a complete list of exec error messages: (defined in 
  8736. the header file "exec/errors.h")
  8737.  
  8738. IOERR_OPENFAIL
  8739.     
  8740. The device (unit) could not be opened. (If you are denied access to the 
  8741. parallel device you should receive the ParErr_DevBusy flag instead of this exec 
  8742. message, but internally this flag is used.) 
  8743.  
  8744. IOERR_ABORTED
  8745.     
  8746. When you abort a previously started request by calling the AbortIO() function, 
  8747. the io_Error filed of that request is set to IOERR_ABORTED. If you find a 
  8748. request block with this flag set, you know that it has been aborted.
  8749.  
  8750. IOERR_NOCMD
  8751.     
  8752. You tried to use a command that is not supported by the parallel device.
  8753. IOERR_BADLENGTH
  8754.     
  8755. The length of the request was not valid.
  8756.  
  8757. 8.3.8  CLEAN UP
  8758.  
  8759. As usual on the Amiga you must remember to close and return everything you have 
  8760. opened or allocated. If you do not close the parallel device after you, other 
  8761. programs will then not be able to use the it. PLEASE be very careful about 
  8762. this!
  8763.  
  8764. The routine is very similar to how you close the serial device which was 
  8765. described in the previous chapter. However this is so important that it can not 
  8766. be said too often.
  8767.  
  8768. Here is a list of what you have to do:
  8769.  
  8770. 1. All requests you have started with SendIO() or BeginIO() (asynchronous 
  8771. commands) must either have been completed or aborted before you may close the 
  8772. device. It is a very common error to forget this, and it can be hard to find 
  8773. this bug. Usually the program will work fine (the command was completed in 
  8774. time), but now and then your program will crash (the command was completed 
  8775. after the device have been closed).
  8776.  
  8777. A simple way is to abort all commands that have not reported that they have 
  8778. been completed, but this is not always good way to do it. (The last commands 
  8779. may be important and should therefore not be aborted.)
  8780.  
  8781. If you do not want to abort the command, you should instead wait for it to be 
  8782. completed. The WaitIO() function is simple to use, and will put your program to 
  8783. sleep while waiting, so no computer time is wasted. If the request has already 
  8784. been completed, the function will return immediately. WaitIO() will also remove 
  8785. the message from the reply port. It is a very useful and simple function to 
  8786. use, but do NOT try to wait for a request that has not been started!
  8787.  
  8788. Here is an example on how to wait for a request to be completed: (If the 
  8789. request already has been completed it does not matter, WaitIO() will then 
  8790. simply return immediately. Note that we do not have to remove any messages from 
  8791. the reply port if we use WaitIO().)
  8792.  
  8793.        /* Store possible error numbers here: */
  8794.        UBYTE error;
  8795.      
  8796.        /* ... */
  8797.      
  8798.        /* Wait for the request to be completed: */
  8799.        error = WaitIO( ioreq );
  8800.      
  8801.        /* Everything OK? */
  8802.        if( error )
  8803.          printf( "Something went wrong!" );
  8804.  
  8805.        /* Well, successful or not, we may now */
  8806.        /* close the device!                   */
  8807.  
  8808.  
  8809. To abort a request, simply use the AbortIO() function:
  8810.  
  8811.        /* Try to abort a previously started request:  */
  8812.        /* (Do not try to abort a request that has not */
  8813.        /* been started.)                              */
  8814.        AbortIO( ioreq );
  8815.  
  8816. 2. When all requests have been completed or aborted you may close the parallel 
  8817. device. (Requests that have been executed by calling the DoIO() function have 
  8818. already been completed before your program wakes up, and thus you do not need 
  8819. to wait for these.)
  8820.      
  8821. The parallel device is closed as all other devices, by calling the 
  8822. CloseDevice() function. Here is an example:
  8823.  
  8824.        /* Close the Parallel Device: */ 
  8825.        if( !parallel_dever )
  8826.          CloseDevice( ioreq );
  8827.  
  8828. 3. You should now return all request blocks you have allocated. If you 
  8829. allocated a standard sized request block by calling the CreateStdIO() function, 
  8830. you should free it by calling the DeleteStdIO() function. However, if you have 
  8831. allocated an extended request block (like the parallel device's request blocks) 
  8832. by calling the CreateExtIO() function, you MUST call the DeleteExtIO(), and of 
  8833. course remove the same amount of data as you allocated.
  8834.  
  8835. How to delete a standard request block: (Since it is of the standard size, you 
  8836. do not need to specify any size.)
  8837.      
  8838.        /* Deallocate a standard sized request block: */
  8839.        DeleteStdIO( ioreq );
  8840.  
  8841.      With extended request block you have to specify the size
  8842.      as you did when you allocated it:
  8843.  
  8844.        /* Deallocate an extended request block: */
  8845.        /* (The size may vary depending on what  */
  8846.        /* device it was used for.)              */
  8847.        DeleteExtIO( ioreq, sizeof( struct IOExtSer ) );
  8848.  
  8849. Note that ALL request blocks that have been allocated, must be removed!
  8850.  
  8851. 4. Finally you should close all message ports you have previously opened. 
  8852. Simply use the DeletePort() function as this example demonstrates:
  8853.  
  8854.        /* Remove the replyport: */
  8855.        DeletePort( replymp);
  8856.  
  8857. Please be careful with how your program terminates! Your program should not 
  8858. only run fine, but it should also allow other programs to run after and 
  8859. simultaneously. Remember that your program must also be able to quit nice and 
  8860. neatly even if it had to terminate too early because of some fatal error. The 
  8861. cleaning up should only be done where it is needed, and if youhave not 
  8862. allocated the memory or opened the device before your program quits, you should 
  8863. of course NOT try to free these resources! If you do the Amiga will most 
  8864. certainly crash! Too many programs contain this very annoying error. Make sure 
  8865. yours will not be the same.
  8866.  
  8867. The best way to manage this cleaning up routine is to write a separate function 
  8868. which checks each thing before it frees it. The idea is that you may call this 
  8869. routine at any time, and it will still manage to clear everything properly. 
  8870. Here is an example: (Note how we check each thing to see if it should be 
  8871. removed!)
  8872.  
  8873.   /* Close and return everything that has been */
  8874.   /* opened and allocated before we quit:      */
  8875.   void clean_up()
  8876.   {
  8877.     /* 1. Close the Parallel Device: */ 
  8878.     if( !parallel_dever )
  8879.       CloseDevice( parallel_req );
  8880.   
  8881.     /* 2. Deallocate the parallel request block: */
  8882.     if( parallel_req )
  8883.       DeleteExtIO( parallel_req, sizeof( struct IOExtPar ) );
  8884.   
  8885.     /* 3. Remove the replyport: */
  8886.     if( replymp )
  8887.       DeletePort( replymp);
  8888.   
  8889.     /* 4. Quit: */
  8890.     exit( 0 );
  8891.   }
  8892.  
  8893. Comments: 
  8894. 1. The parallel_dever is a variable that we have declared ourself, and is used 
  8895. only to determine if the device has been opened or not. (The variable is set to 
  8896. TRUE before the program starts, and is only changed to FALSE after a successful 
  8897. opening of the device. See the included examples for more information.) We 
  8898. should of course only close the device if the parallel_dever is FALSE. 
  8899. ("parallel_dever" stands for "parallel device error".)
  8900.  
  8901. 2. The parallel_req is a pointer which is either pointing to a request block, 
  8902. or NULL if no request block has been allocated. We should of course only free 
  8903. the request block if there exist one.
  8904.  
  8905. 3. The replymp is a pointer which is either pointing to a message port, or NULL 
  8906. if no message port has been opened. And of course, we should only close the 
  8907. message port if it has been opened.
  8908.  
  8909. 4. Finally our program may terminate. The exit() function is placed here so we 
  8910. are sure the program will quit. (If we did not have this exit(), our function 
  8911. would then return the control to our program again, and then we would be in 
  8912. deep s*#^%.)
  8913.  
  8914. 8.4  A COMPLETE EXAMPLE
  8915.  
  8916. Here is a complete example that opens the parallel device, sets all necessary 
  8917. parameters, sends some data, collects some data (well actually only 0 bytes) 
  8918. and finally cleans up and quits. Since we send the read and write requests 
  8919. after each other (the first is completed before the other is started) we only 
  8920. need to use one request block.
  8921.  
  8922. #include <exec/types.h>
  8923. #include <devices/parallel.h>
  8924.  
  8925.  
  8926. /* Declare a pointer to our reply port: */
  8927. struct MsgPort *replymp = NULL;
  8928.  
  8929. /* Declare a pointer to our parallel request block: */
  8930. struct IOExtPar *parallel_req = NULL;
  8931.  
  8932. /* Store the parallel device error here: */
  8933. UWORD parallel_dever = TRUE;
  8934.  
  8935. /* Declare our own data buffer: */
  8936. /* (Last byte is a NULL sign.)  */
  8937. BYTE buffer[] = "Anders Bjerin was here...";
  8938.  
  8939.  
  8940. /* Declare our functions: */
  8941. void main();
  8942. void clean_up( STRPTR text );
  8943.  
  8944. void main()
  8945. {
  8946.   int loop;
  8947.   UBYTE *ptr;
  8948.   UBYTE error;
  8949.  
  8950.   /* The eight end-of-file characters: */
  8951.   UBYTE eof_char[8]={ 0x00, 0x00, 0x00, 0x00,
  8952.                       0x00, 0x00, 0x00, 0x00 };
  8953.  
  8954.  
  8955.   
  8956.   /* OPEN THE PARALLEL DEVICE: */
  8957.  
  8958.   /* Get a reply port: (No name, priority 0) */
  8959.   replymp = (struct MsgPort *)
  8960.     CreatePort( NULL, 0 );
  8961.   if( !replymp )
  8962.     clean_up( "Could not create the reply port!" );
  8963.  
  8964.   /* Create a parallel request block: */
  8965.   parallel_req = (struct IOExtPar *)
  8966.     CreateExtIO( replymp, sizeof( struct IOExtPar ) );
  8967.   if( !parallel_req )
  8968.     clean_up( "Not enough memory for the parallel request block!" );
  8969.  
  8970.  
  8971.   /* Since we want exclusive access mode, we do not set the */
  8972.   /* parallel flag "PARF_SHARED", which otherwise must have */
  8973.   /* been set before the device is opened. To make sure the */
  8974.   /* field is empty we set it to 0:                         */
  8975.   parallel_req->io_ParFlags = 0;
  8976.  
  8977.  
  8978.   /* Open the Parallel Device: */
  8979.   parallel_dever = OpenDevice( PARALLELNAME, 0, parallel_req, 0 );
  8980.   if( parallel_dever )
  8981.     clean_up( "Could not open the Parallel Device!" );
  8982.  
  8983.  
  8984.  
  8985.   
  8986.   /* SET THE REST OF THE PARALLEL PARAMETERS (not many): */
  8987.   
  8988.   /* Check for end-of-file characters: */
  8989.   parallel_req->io_ParFlags = PARF_EOFMODE;
  8990.  
  8991.   /* No additional flags: */
  8992.   parallel_req->io_PExtFlags = NULL;
  8993.   
  8994.   /* Set all eight end of file characters: */
  8995.   ptr = (UBYTE *) &(parallel_req->io_PTermArray);
  8996.   for( loop=0; loop < 8; loop++ )
  8997.   {
  8998.     /* Copy character after character: */
  8999.     *ptr = eof_char[ loop ];
  9000.  
  9001.     /* Step one byte forward: */
  9002.     ptr++;
  9003.   }
  9004.   
  9005.   /* All values have now been set, lets */
  9006.   /* do a PDCMD_SETPARAMS request:      */
  9007.   parallel_req->IOPar.io_Command = PDCMD_SETPARAMS;
  9008.   
  9009.   /* Do our request: */
  9010.   error = DoIO( parallel_req );
  9011.   if( error )
  9012.     clean_up( "Could not set the parallel parameters!" );
  9013.  
  9014.  
  9015.  
  9016.   /* SEND DATA TO THE PARALLEL PORT: */
  9017.  
  9018.   /* We want to send (write) some data: */
  9019.   parallel_req->IOPar.io_Command = CMD_WRITE;
  9020.  
  9021.   /* Give the start address of our data: */
  9022.   parallel_req->IOPar.io_Data = (APTR) buffer;
  9023.  
  9024.   /* We want to send data until we find a NULL sign: */
  9025.   /* (The NULL sign was specified in the EOF-char.)  */
  9026.   parallel_req->IOPar.io_Length = -1;
  9027.  
  9028.   /* Do our request: */
  9029.   error = DoIO( parallel_req );
  9030.   if( error )
  9031.     clean_up( "Could not send data to the parallel port!" );
  9032.  
  9033.  
  9034.  
  9035.   /* READ DATA FROM THE PARALLEL DEVICE: */
  9036.   
  9037.   /* We want to read some data: */
  9038.   parallel_req->IOPar.io_Command = CMD_READ;
  9039.  
  9040.   /* Give the start address of our buffer: */
  9041.   parallel_req->IOPar.io_Data = (APTR) buffer;
  9042.  
  9043.   /* We want to read 0 bytes: */
  9044.   /* (Since most of you do not have anything which sends data */
  9045.   /* to the parallel device, I do not want to wait for more   */
  9046.   /* than 0 bytes to arrive.)                                 */
  9047.   parallel_req->IOPar.io_Length = 0;
  9048.  
  9049.   /* Do our request: */
  9050.   error = DoIO( parallel_req );
  9051.   if( error )
  9052.     clean_up( "Could not read data from the parallel port!" );
  9053.  
  9054.  
  9055.  
  9056.   /* THE END: */
  9057.   clean_up( "The End" );
  9058. }
  9059.  
  9060.  
  9061. /* Close and return everything that has been */
  9062. /* opened and allocated before we quit:      */
  9063. void clean_up( STRPTR text )
  9064. {
  9065.   /* Close the Parallel Device: */
  9066.   if( !parallel_dever )
  9067.     CloseDevice( parallel_req );
  9068.  
  9069.   /* Deallocate the parallel request block: */
  9070.   if( parallel_req )
  9071.     DeleteExtIO( parallel_req, sizeof( struct IOExtPar ) );
  9072.  
  9073.   /* Remove the replyport: */
  9074.   if( replymp )
  9075.     DeletePort( replymp);
  9076.  
  9077.   /* Print the message: */
  9078.   printf( "\n%s\n", text );
  9079.  
  9080.   /* Quit: */
  9081.   exit( 0 );
  9082. }
  9083.  
  9084. 8.5  OTHER USEFUL COMMANDS
  9085.  
  9086. Although reading and writing are most commonly used commands, there exist some 
  9087. other functions that can sometimes be needed. Here is a complete list of 
  9088. commands that can be sent with help of a request block:
  9089.  
  9090. 1. Flush, removes all queued requests.
  9091. 2. Query, get some information from the parallel device.
  9092. 3. Reset, reinitializes the parallel device.
  9093. 4. Start, restarts the parallel communication.
  9094. 5. Stop, temporary stops the parallel communication.
  9095.  
  9096. 8.5.1  FLUSH
  9097.  
  9098. If several requests are sent to the parallel device they are all queued on a 
  9099. FIFO (First In First Out) basis. The command "CMD_FLUSH" can then be used to 
  9100. remove all these queued commands. Here is an example:
  9101.  
  9102.   /* We want to remove all queued requests: */
  9103.   ioreq->IOPar.io_Command = CMD_FLUSH;
  9104.  
  9105.   /* Do our request: */
  9106.   error = DoIO( ioreq );
  9107.  
  9108.   /* OK? */
  9109.   if( error )
  9110.     printf( "Could not remove the queued requests!\n" );
  9111.  
  9112. 8.5.2  QUERY
  9113.  
  9114. The command "PDCMD_QUERY" can be used to get some information from the parallel 
  9115. device. It is useful if you want to see if the paper is out, printer is busy or 
  9116. if it is currently writing or reading. All this can be found in the "io_Status" 
  9117. field of the request structure, as this table shows:
  9118.  
  9119.   Name           Bit  Hex   Active  Description
  9120.   ------------------------------------------------------------
  9121.   IOPTF_PARBUSY    0  0x01  Low     Printer is selected
  9122.   IOPTF_PAPEROUT   1  0x02  Low     Paper out
  9123.   IOPTF_PARSEL     2  0x04  Low     Printer is busy
  9124.   IOPTF_RWDIR      3  0x08  -       Reading (0) or Writing (1) 
  9125.   -                4  0x10  -       Reserved
  9126.   -                5  0x20  -       Reserved
  9127.   -                6  0x40  -       Reserved
  9128.   -                7  0x80  -       Reserved
  9129.  
  9130. Here is an example:
  9131.  
  9132.   /* Check the parallel device: */
  9133.   ioreq->IOPar.io_Command = PDCMD_QUERY;
  9134.  
  9135.   /* Do our request: */
  9136.   error = DoIO( ioreq );
  9137.  
  9138.   /* OK? */
  9139.   if( error )
  9140.     printf( "Could not get any information from the device!\n" );
  9141.   else
  9142.   {
  9143.     /* Check the "io_Status" field: */
  9144.     if( ioreq->io_Status & IOPTF_PARBUSY )
  9145.       printf( "Printer is busy.\n" );
  9146.  
  9147.     if( ioreq->io_Status & IOPTF_PAPEROUT )
  9148.       printf( "Paper out!\n" );
  9149.  
  9150.     if( ioreq->io_Status & IOPTF_PARSEL )
  9151.       printf( "Printer selected!\n" );
  9152.  
  9153.     printf( "Device is %s\n",
  9154.       ioreq->io_Status & IOPTF_RWDIR ? "Writing" : "Reading" ); 
  9155.   }
  9156.  
  9157. 8.5.3  RESET
  9158.  
  9159. Send the command "CMD_RESET" to reset the parallel device. All commands that 
  9160. are queued to the device will be removed, commands that are currently executed 
  9161. will be aborted all parallel flags are resetted. Here is an example:
  9162.  
  9163.   /* We want to reset the parallel device: */
  9164.   ioreq->IOPar.io_Command = CMD_RESET;
  9165.  
  9166.   /* Do our request: */
  9167.   error = DoIO( ioreq );
  9168.  
  9169.   /* OK? */
  9170.   if( error )
  9171.     printf( "Could not reset the parallel device!\n" );
  9172.  
  9173. 8.5.4  START
  9174.  
  9175. After you have stopped the parallel communication by sending an CMD_STOP 
  9176. command, you may want to start the communication again. It is done by sending a 
  9177. CMD_START command. Here is an example:
  9178.  
  9179.   /* We want to start parallel communication again: */
  9180.   ioreq->IOPar.io_Command = CMD_START;
  9181.  
  9182.   /* Do our request: */
  9183.   error = DoIO( ioreq );
  9184.  
  9185.   /* OK? */
  9186.   if( error )
  9187.     printf( "Could not start the parallel communication!\n" );
  9188.  
  9189. 8.5.5  STOP
  9190.  
  9191. To temporary stop all parallel communication you send a CMD_STOP command. The 
  9192. communication will then first start again when a CMD_START command is 
  9193. broadcasted. Here is an example:
  9194.  
  9195.   /* We want to temporary stop all parallel communication: */
  9196.   ioreq->IOPar.io_Command = CMD_STOP;
  9197.  
  9198.   /* Do our request: */
  9199.   error = DoIO( ioreq );
  9200.  
  9201.   /* OK? */
  9202.   if( error )
  9203.     printf( "Could not stop the parallel communication!\n" );
  9204.  
  9205. 8.6  FUNCTIONS
  9206.  
  9207. DoIO()
  9208.  
  9209. DoIO() is used to send requests to a device, and waits for it to be completed. 
  9210. While the program is waiting it is put to sleep so it will not waste any 
  9211. computer time. DoIO() will return first when the request have been completed or 
  9212. failed, and no message is therefore sent to the reply port.
  9213.  
  9214. Synopsis:
  9215.     
  9216. error = DoIO( req );
  9217.  
  9218. error:
  9219.     
  9220. (long) DoIO() will return first when the request has been completed or 
  9221. something has failed. If the request was successfully completed zero is 
  9222. returned, else an error number is returned. What error number depends on which 
  9223. device was used.
  9224.  
  9225. req:
  9226.     
  9227. (struct IORequest *) Pointer to the request you want to have executed.
  9228.  
  9229. SendIO()
  9230.  
  9231. SendIO() is used to send requests to a device, but will return immediately 
  9232. without any delay. To check if the request have been completed use the 
  9233. CheckIO() function, or look at the request's reply port for any messages. Once 
  9234. the request has been completed you must remove the message at the reply port. 
  9235. (CheckIO() will not do it.) To remove a message use the function Remove(). Note 
  9236. that you may NOT close the device before all requests have been completed or 
  9237. aborted!
  9238.  
  9239. Synopsis:
  9240.     
  9241. SendIO( req )
  9242.  
  9243. req:
  9244.     
  9245. (struct IORequest *) Pointer to the request you want to have executed.
  9246.  
  9247. CheckIO()
  9248.  
  9249. CheckIO() is used to check if a previously started request has been completed. 
  9250. Note that this function will not remove the message at the reply port. This 
  9251. must be done with the Remove() function.
  9252.  
  9253. Synopsis:
  9254.     
  9255. ptr = CheckIO( req );
  9256.  
  9257. ptr:
  9258.     
  9259. (long) CheckIO() will either return NULL if the request have not been completed 
  9260. or it will return a pointer to the request block.
  9261.  
  9262. req:
  9263.     
  9264. (struct IORequest *) Pointer to the request you want to check.
  9265.  
  9266. WaitIO()
  9267.  
  9268. WaitIO() will wait for the request to be completed, and while the program is 
  9269. waiting it is put to sleep so no computer time is wasted.
  9270.  
  9271. Synopsis:
  9272.     
  9273. error = WaitIO( req );
  9274.  
  9275. error:
  9276.     
  9277. (long) WaitIO() will return first when the request, that has previously been 
  9278. sent, has been completed or something has failed. If the request was 
  9279. successfully completed zero is returned, else an error number is returned. What 
  9280. error number depends on which device was used.
  9281.  
  9282. req:
  9283.     
  9284. (struct IORequest *) Pointer to the request you want to wait for to be 
  9285. completed. Note that the request must have already been sent to the device by 
  9286. either a SendIO() or BeginIO() function call.
  9287.  
  9288. BeginIO()
  9289.  
  9290. BeginIO() is a low level form of the SendIO() function. The advantage with 
  9291. BeginIO() is that no fields of the request block will be altered as which is 
  9292. the case with SendIO(). With the parallel device you should only use SendIO().
  9293.  
  9294. Synopsis:
  9295.     
  9296. BeginIO( req )
  9297.  
  9298. req:
  9299.     
  9300. (struct IORequest *) Pointer to the request you want to have executed.
  9301.  
  9302. AbortIO()
  9303.  
  9304. AbortIO() will try to abort a previously started request. This function should 
  9305. be used sparsely since it does not look so good if you start a request and the 
  9306. try to stop it. (Better not start it at all.) However, it is easy, and can 
  9307. sometimes be very useful.
  9308.  
  9309. A request that is aborted will have its io_Error field set to IOERR_ABORTED 
  9310. (defined in header file "exec/errors.h").
  9311.  
  9312. Synopsis:
  9313.     
  9314. AbortIO( req )
  9315.  
  9316. req:
  9317.     
  9318. (struct IORequest *) Pointer to the request you want to abort.
  9319.  
  9320. CloseDevice()
  9321.  
  9322. CloseDevice() will (surprise!) close a device. Note that you should NOT close 
  9323. the device before all started asynchronous requests have either been completed 
  9324. or aborted.
  9325.  
  9326. Synopsis:
  9327.     
  9328. CloseDevice( ioreq );
  9329.  
  9330. ioreg:
  9331.     
  9332. (struct IORequest *) Pointer to the device's request block.
  9333.  
  9334. OpenDevice()
  9335.  
  9336. OpenDevice() will try to open the specified device.
  9337.   
  9338. Synopsis:
  9339.     
  9340. error = OpenDevice( name, unit, req, flags );
  9341.  
  9342. error:
  9343.     
  9344. (long) If OpenDevice() managed to open the device it returns 0, else an error 
  9345. number is returned. If you try to open the parallel device, and there is 
  9346. already a program that is using it, the error message "ParErr_DevBusy" is 
  9347. returned.
  9348.  
  9349. name:
  9350.     
  9351. (char *) Name of the device you want to open. The name of the parallel device 
  9352. is defined as PARALLELNAME in header file "devices/parallel.h".
  9353.  
  9354. unit:
  9355.     
  9356. (long) Which unit you want to open. Since there exist only one parallel port, 
  9357. this field is ignored.
  9358. req:
  9359.     
  9360. (struct IORequest *) Pointer to a request block. For the parallel device it 
  9361. must be a pointer to an extender parallel request block (struct IOExtPar).
  9362.  
  9363. flags:
  9364.     
  9365. (long) Any special mode is set here. Ignored by the parallel device.
  9366.  
  9367. 8.7  COMMANDS
  9368.  
  9369. Here is a complete list of commands you may send to the parallel device. For 
  9370. full documentation se examples above. 
  9371.  
  9372. The special parallel device commands: (Defined in header file 
  9373. "devices/parallel.h")
  9374.  
  9375. PDCMD_QUERY
  9376.     
  9377. Check the status of the parallel device.
  9378. PDCMD_SETPARAMS
  9379.     
  9380. Set the parameters of the parallel device.
  9381.  
  9382. The rest of the commands you may use are normal exec commands, and are defined 
  9383. in header file "exec/io.h".
  9384.  
  9385. CMD_RESET
  9386.     
  9387. Resets all parameters of the parallel device.
  9388. CMD_READ
  9389.     
  9390. Read data from the parallel port.
  9391. CMD_WRITE
  9392.     
  9393. Write data to the parallel port.
  9394. CMD_STOP
  9395.     
  9396. Temporary stops all parallel communication.
  9397. CMD_START
  9398.     
  9399. Restarts parallel communication.
  9400. CMD_FLUSH
  9401.     
  9402. Removes all queued requests.
  9403.  
  9404. 8.8  EXAMPLES
  9405.  
  9406. Example 1
  9407. This program demonstrates how you can use the Parallel Device. It does not do 
  9408. very much since I do not know what you have connected to your parallel port, 
  9409. but with small modifications you should be able to write your own parallel 
  9410. communication packages.
  9411.  
  9412. Example 2
  9413. This example is rather similar to Example 1, but this time we do not wait for 
  9414. the parallel port to complete our request. Instead we do somethings (well not 
  9415. very much) and now and then checks if the request has been completed.
  9416.  
  9417. Example 3
  9418. This example is rather similar to Example 1, but this time we do not wait for 
  9419. the parallel port to complete our request. We are also trying to read and write 
  9420. at the same time. To be able to do several requests simultaneously we need one 
  9421. request block for each command. In this example we use three separate request 
  9422. blocks.
  9423.  
  9424. Example4
  9425. This example does not do anything, but it consists of several useful functions 
  9426. that you can use yourself after small modifications. The functions demonstrates 
  9427. all commands there exist for the parallel device, so if you had problems in 
  9428. understanding how a command was used you can look here.
  9429.  
  9430.  
  9431. PRINTER DEVICE
  9432.  
  9433. 9.1  INTRODUCTION
  9434.  
  9435. The printer device works close together with both the serial and the parallel 
  9436. device. If you want to control a printer, there are several advantages of using 
  9437. the printer device instead of the serial or parallel device.
  9438.  
  9439. First you do not need to know to which port (serial or parallel) the printer is 
  9440. connected to. The printer device will simply look at the "Preferences" 
  9441. settings, and will there find this type of information.
  9442.  
  9443. Secondly, since the user has specified what type of printer is used with 
  9444. Preferences, the printer device will also know how to translate the universal 
  9445. printer commands (listed below) into that printers own special commands. This 
  9446. is very good since if your program can handle some of the commands listed 
  9447. below, it will work on most printers.
  9448.  
  9449. Finally, there exist a lot of special features like printing graphics, 
  9450. translating foreign characters, and changing the printer settings. All this is 
  9451. supported by the printer device.
  9452.  
  9453. 9.2  PRINTER DEVICE
  9454.  
  9455. The printer device is, as said above, very close connected to the serial and 
  9456. parallel devices. The printer device can be said to be on a "higher level". The 
  9457. reason of this is that the printer device will with help of Preferences control 
  9458. either the serial or parallel device. (See illustration "Printer Device".)
  9459.  
  9460.           ----------------
  9461.           |Printer Device|
  9462.           ----------------
  9463.                   |
  9464.                   V
  9465.             -------------
  9466.             |Preferences|
  9467.             -------------
  9468.              |         |
  9469.              V         V
  9470.   ---------------   -----------------
  9471.   |Serial Device|   |Parallel Device|
  9472.   ---------------   -----------------
  9473.          |                  |
  9474.          V                  V
  9475.    -------------     ---------------
  9476.    |Serial Port|     |Parallel Port|
  9477.    -------------     ---------------
  9478.  
  9479. Although the complexity of printing, the printer device is very easy and 
  9480. straight forward to use. It works like all other normal devices, and with only 
  9481. some extra structures and commands a lot can be done.
  9482.  
  9483. 9.2.1  THE PRINTER DEVICE'S REQUESTBLOCKS
  9484.  
  9485. As with all devices you use a request block to communicate with the printer 
  9486. device. You initialize the request block as will be described below, and then 
  9487. you simply send it to the device with for example an DoIO() or SendIO() 
  9488. command.
  9489.  
  9490. Since the printer device can handle a lot of special commands there exist three 
  9491. different request blocks. Which should be used depends on what you want to do.
  9492.  
  9493. 1. The standard request block (struct IOStdReq) should be used for all "normal" 
  9494. commands.
  9495.  
  9496. 2. The special printer command block (struct IOPrtCmdReq) should be used when 
  9497. you want to send special commands to the printer.
  9498.  
  9499. 3. The special graphic request block (struct IODRPReq) should be used when you 
  9500. want to print graphics (dump a Rastport to a printer).
  9501.  
  9502. To make it easier for you the printer device can handle a union with these 
  9503. three structures. This means that you only have to bother about one singe 
  9504. request block. The union look like this:
  9505.  
  9506. union printerIO
  9507. {
  9508.   struct IOStdReq ios;
  9509.   struct IODRPReq iodrp;
  9510.   struct IOPrtCmdReq iopc;
  9511. };
  9512.  
  9513. Each structure of this union will be described in more detail below.
  9514.  
  9515. 9.2.1.1  STANDARD REQUEST BLOCK
  9516.  
  9517. The printer device use a standard request block (struct IOStdReq) for all 
  9518. normal commands. It looks like this: (Defined in header file "exec/io.h". Note 
  9519. that you will probably only use some of these fields, so you do not need to 
  9520. bother too much about it.)
  9521.  
  9522. struct IOStdReq
  9523. {
  9524.   struct Message io_Message;
  9525.   struct Device  *io_Device;
  9526.   struct Unit    *io_Unit;
  9527.   UWORD  io_Command;
  9528.   UBYTE  io_Flags;
  9529.   BYTE   io_Error;
  9530.   ULONG  io_Actual;
  9531.   ULONG  io_Length;
  9532.   APTR   io_Data;
  9533.   ULONG  io_Offset;
  9534. };
  9535.  
  9536. io_Message:
  9537.     
  9538. This is the message that will be sent to your reply port once the device has 
  9539. successfully or not finished your request
  9540.  
  9541. io_Device:
  9542.     
  9543. Pointer to the device which is using this request block.
  9544.  
  9545. io_Unit:
  9546.     
  9547. If the device has several units (like the Trackdisk Device) this is a pointer 
  9548. to one of these units.
  9549.  
  9550. io_Command:
  9551.     
  9552. It is here you set the command flag which tells the device what you actually 
  9553. want to do. There exist a lot of different commands depending on which device 
  9554. you are using.
  9555.             
  9556.     
  9557. The commands can be divided into to groups. The first group of commands is the 
  9558. standard device commands like read and write. They are used by most devices, 
  9559. and are defined in the same header file as the request structure ("exec/io.h"). 
  9560. Se chapter 17 "Devices" for more information about these commands.
  9561.  
  9562.     
  9563. (The printer device can only handle the commands which are marked with a star 
  9564. "*".)
  9565.  
  9566.     
  9567. CMD_RESET:  (*) Reset the device. Removes all queued commands and sets all 
  9568. fields to the default settings.
  9569.  
  9570.     
  9571. CMD_READ:   Read (collect) data from the device.
  9572.  
  9573.     
  9574. CMD_WRITE:  (*) Write (send) data to the device.
  9575.  
  9576.     
  9577. CMD_UPDATE: Update the device.
  9578.  
  9579.     
  9580. CMD_CLEAR:  Clears the device's own input buffer.
  9581.  
  9582.     
  9583. CMD_STOP:   (*) Temporarily stops the device.
  9584.  
  9585.     
  9586. CMD_START:  (*) Restarts the device after it has been stopped.
  9587.  
  9588.     
  9589. CMD_FLUSH:  (*) Removes all queued commands.
  9590.             
  9591.     
  9592. The second group contains all special device commands. Each device chapter 
  9593. (18-29) contains a complete list of each device's special commands, together 
  9594. with full instructions on how to use them.
  9595.             
  9596.     
  9597. The printer device uses four special commands, but only two of these may be 
  9598. used by the standard request block: (See the other structures for more 
  9599. information about the two other commands.)
  9600.  
  9601.     
  9602. PRD_RAWWRITE:   Sends raw (not translated) characters.
  9603.  
  9604.     
  9605. PRD_QUERY:      Send this command if you want to receive some information about 
  9606. the printer device.
  9607.  
  9608. io_Flags:
  9609.     
  9610. This flag field is used by only some of the devices.
  9611.  
  9612. io_Error:
  9613.     
  9614. This field is set to 0 if the device managed to do your request, else an error 
  9615. number is stored here. Depending on which device you are using different error 
  9616. messages are used. However, there exist four standard error messages (defined 
  9617. in header file "exec/errors.h"):
  9618.             
  9619.     
  9620. IOERR_OPENFAIL:  Could not open the device.
  9621.  
  9622.     
  9623. IOERR_ABORTED:   The request was aborted.
  9624.  
  9625.     
  9626. IOERR_NOCMD:     A command that was not supported by the device was used.
  9627.  
  9628.     
  9629. IOERR_BADLENGTH: Bad length of the command - data.
  9630.  
  9631.     
  9632. If the printer device failed to do your request, one of these error flags is 
  9633. returned:
  9634.  
  9635.     
  9636. PDERR_CANCEL:         User cancelled the request.
  9637.  
  9638.     
  9639. PDERR_NOTGRAPHICS:    The printer which the user has can not handle graphics.   
  9640.  
  9641.  
  9642.     
  9643. PDERR_BADDIMENSION:   The printer dimension is not valid.
  9644.  
  9645.     
  9646. PDERR_INTERNALMEMORY: Not enough memory for the printer device's internal 
  9647. variables.
  9648.  
  9649.     
  9650. PDERR_BUFFERMEMORY:   Not enough memory for the print buffer.
  9651.  
  9652. io_Actual:
  9653.     
  9654. The number of bytes that was actually collected or sent. If this number is not 
  9655. the same as the number of bytes you wanted to collect/send, the communication 
  9656. may have been interrupted.
  9657.  
  9658. io_Length:
  9659.     
  9660. The number of bytes you want to send or collect.
  9661.  
  9662. io_Data:
  9663.     
  9664. Pointer to where the data is which should be sent, or pointer to where all data 
  9665. which is collected should be stored.
  9666.  
  9667. io_Offset:
  9668.     
  9669. Offset value used by some devices.
  9670.  
  9671. 9.2.1.2  SPECIAL PRINTER COMMAND STRUCTURE
  9672.  
  9673. When you want to send special printer commands like "italics on", "boldface 
  9674. off", "subscript on" etc (a complete table of printer commands is listed below) 
  9675. you can do it in two ways:
  9676.  
  9677. (A) You send the printer command's "Escape Sequence" in the same way you send 
  9678. normal text. These escape sequences will be translated by Preferences and then 
  9679. sent to the printer.
  9680.      
  9681. (B) You send the printer command with help of a special printer command request 
  9682. block (struct IOPrtCmdReq).
  9683.  
  9684. Both options will work equally well, but your code will look better if you use 
  9685. the special IOPrtCmdReq structure. (It is easier to understand what is 
  9686. happening, since escape sequences are usually not very informative and 
  9687. definitely not easy to remember.) All printer commands are also defined in the 
  9688. header file "devices/printer.h", and you are therefore recommended to use them 
  9689. instead of the escape sequences.
  9690.  
  9691.  
  9692. struct IOPrtCmdReq
  9693. {
  9694.   struct Message io_Message;
  9695.   struct Device  *io_Device;
  9696.   struct Unit    *io_Unit;
  9697.   UWORD  io_Command;
  9698.   UBYTE  io_Flags;
  9699.   BYTE   io_Error;
  9700.   UWORD  io_PrtCommand;
  9701.   UBYTE  io_Parm0;
  9702.   UBYTE  io_Parm1;
  9703.   UBYTE  io_Parm2;
  9704.   UBYTE  io_Parm3;
  9705. };
  9706.  
  9707. io_Message:
  9708.     
  9709. This is the message that will be sent to your reply port once the device has 
  9710. successfully or not finished your request
  9711.  
  9712. io_Device:
  9713.     
  9714. Pointer to the printer device which is using this request block.
  9715.  
  9716. io_Unit:
  9717.     
  9718. Not used by the printer device.
  9719.  
  9720. io_Command:
  9721.     
  9722. Set this field to PRD_PRTCOMMAND, and the device will know that you want to 
  9723. send commands to the printer. NOTE! The flag "PRD_PRTCOMMAND" should only be 
  9724. used by IOPrtCmdReq structures!!!
  9725.  
  9726. io_Flags:
  9727.     
  9728. Not used.
  9729.  
  9730. io_Error:
  9731.     
  9732. This field is set to 0 if the printer device managed to do your request, else 
  9733. an error number is stored here:
  9734.  
  9735.     
  9736. PDERR_CANCEL:         User cancelled the request.
  9737.  
  9738.     
  9739. PDERR_NOTGRAPHICS:    The printer which the user has can not handle graphics.   
  9740.  
  9741.  
  9742.     
  9743. PDERR_BADDIMENSION:   The printer dimension is not valid.
  9744.  
  9745.     
  9746. PDERR_INTERNALMEMORY: Not enough memory for the printer device's internal 
  9747. variables.
  9748.  
  9749.     
  9750. PDERR_BUFFERMEMORY:   Not enough memory for the print buffer.
  9751.  
  9752. io_PrtCommand:
  9753.     
  9754. The command you want to send to the printer. See below for a complete list of 
  9755. all printer commands. Some of the commands require extra parameters, and these 
  9756. parameters should be set in the following four fields.
  9757.  
  9758. io_Parm0:
  9759.     
  9760. First parameter.
  9761.  
  9762. io_Parm1:
  9763.     
  9764. Second parameter.
  9765.  
  9766. io_Parm2:
  9767.     
  9768. Third parameter.
  9769.  
  9770. io_Parm3:
  9771.     
  9772. Fourth parameter.
  9773.  
  9774. 9.2.1.3  SPECIAL GRAPHIC STRUCTURE
  9775.  
  9776. When you want to print graphics you have to use the special IODRPReq structure. 
  9777. It will dump a specified rastport (see chapter 12 "Low Level Graphics" for more 
  9778. information about rastports) to the printer. Note that this request will only 
  9779. work if the user has a printer that supports graphics.
  9780.  
  9781. The IODRPReq structure look like this: (also defined in header file 
  9782. "devices/printer.h")
  9783.  
  9784. struct IODRPReq
  9785. {
  9786.   struct Message io_Message;
  9787.   struct Device  *io_Device;
  9788.   struct Unit    *io_Unit;
  9789.   UWORD  io_Command;
  9790.   UBYTE  io_Flags;
  9791.   BYTE   io_Error;
  9792.   struct RastPort *io_RastPort;
  9793.   struct ColorMap *io_ColorMap;
  9794.   ULONG  io_Modes;
  9795.   UWORD  io_SrcX;
  9796.   UWORD  io_SrcY;
  9797.   UWORD  io_SrcWidth;
  9798.   UWORD  io_SrcHeight;
  9799.   LONG   io_DestCols;
  9800.   LONG   io_DestRows;
  9801.   UWORD  io_Special;
  9802. };
  9803.  
  9804.  
  9805. io_Message:
  9806.     
  9807. This is the message that will be sent to your reply port once the device has 
  9808. successfully or not finished your request
  9809.  
  9810. io_Device:
  9811.     
  9812. Pointer to the printer device which is using this request block.
  9813.  
  9814. io_Unit:
  9815.     
  9816. Not used by the printer device.
  9817.  
  9818. io_Command:
  9819.     
  9820. Set this field to PRD_PRTCOMMAND, and the device will know that you want to 
  9821. send commands to the printer. NOTE! The flag "PRD_PRTCOMMAND" should only be 
  9822. used by IOPrtCmdReq structures!!!
  9823.  
  9824. io_Flags:
  9825.     
  9826. Not used.
  9827.  
  9828. io_Error:
  9829.     
  9830. This field is set to 0 if the printer device managed to do your request, else 
  9831. an error number is stored here:
  9832.  
  9833.     
  9834. PDERR_CANCEL:         User cancelled the request.
  9835.  
  9836.     
  9837. PDERR_NOTGRAPHICS:    The printer which the user has can not handle graphics.   
  9838.  
  9839.  
  9840.     
  9841. PDERR_BADDIMENSION:   The printer dimension is not valid.
  9842.  
  9843.     
  9844. PDERR_INTERNALMEMORY: Not enough memory for the printer device's internal 
  9845. variables.
  9846.  
  9847.     
  9848. PDERR_BUFFERMEMORY:   Not enough memory for the print buffer.
  9849.  
  9850. io_RastPort:
  9851.     
  9852. Pointer to the RastPort that should be printed.
  9853.  
  9854. io_ColorMap:
  9855.     
  9856. Pointer to the ColorMap structure which contains all information about the 
  9857. RastPort's colours. 
  9858.  
  9859. io_Modes:
  9860.     
  9861. The ViewPort's display modes. The information is used to convert the picture 
  9862. which will be printed to the correct aspects. (On a low resolution screen each 
  9863. pixels is equally wide as tall. However, on a high resolution screen, each 
  9864. pixel is only half as wide as it is tall. The same applies for interlaced and 
  9865. non interlaced screens.) The printer device must also know if you want to print 
  9866. a "normal" picture, or a picture with one of the special display modes like 
  9867. "HAM" or "Extrahalf Brite". The following flags may be used:
  9868.  
  9869.     
  9870. HIRES:  Set this flag if you want to print a high resolution screen. If this 
  9871. flag is not set, the printer device assumes that you are using a low resolution 
  9872. screen.
  9873.  
  9874.     
  9875. LACE:   Set this flag if you want to print an interlaced picture. If this flag 
  9876. is not set, the printer device assumes that you are using a non-interlaced 
  9877. picture.
  9878.  
  9879.     
  9880. HAM:    Set this flag if you want to print a "HAM" picture.
  9881.  
  9882.     
  9883. EXTRA_HALFBRITE: Set this flag if you want to print an "extra halfbrite" 
  9884. picture.
  9885.  
  9886.     
  9887. D
  9888. UALPF: Set this flag if you want to print a dual playfields screen.
  9889.  
  9890.     
  9891. Note that the simplest way is to copy the Viewport structure's "modes" field. 
  9892. You will then not risk to forget one or more display flags.
  9893.  
  9894. io_SrcX:
  9895.     
  9896. X offset of the source picture.
  9897.  
  9898. io_SrcY:
  9899.     
  9900. Y offset of the source picture.
  9901.  
  9902. io_SrcWidth:
  9903.     
  9904. Width of the source picture.
  9905.  
  9906. io_SrcHeight:
  9907.     
  9908. Height of the source picture.
  9909.  
  9910. io_DestCols:
  9911.     
  9912. Width of the printed picture. If the special flag "SPECIAL_MILCOLS" is set, the 
  9913. width is in 1/1000". If the special flag "SPECIAL_FULLCOLS" is set, this field 
  9914. is ignored by the printer device. (The picture will be as wide as possible.) 
  9915. Finally, if the special flag "SPECIAL_FRACCOLS" is set this field specifies the 
  9916. size as a fraction of the maximum width.
  9917.                
  9918. io_DestRows:
  9919.     
  9920. Height of the printed picture. If the special flag "SPECIAL_MILROWS" is set, 
  9921. the height is in 1/1000". If the special flag "SPECIAL_FULLROWS" is set, this 
  9922. field is ignored by the printer device. (The picture will be as tall as 
  9923. possible.) Finally, if the special flag "SPECIAL_FRACROWS" is set this field 
  9924. specifies the size as a fraction of the maximum height.
  9925.  
  9926. io_Special:
  9927.     
  9928. There exist several special graphical printing modes. Here is a complete list 
  9929. of flags that may be used:
  9930.  
  9931.     
  9932. SPECIAL_MILCOLS:    If this flag is set the "io_DestCols" field specifies the 
  9933. width in 1/1000".
  9934.  
  9935.     
  9936. SPECIAL_MILROWS:    If this flag is set the "io_DestRows" field specifies the 
  9937. height in 1/1000".
  9938.  
  9939.     
  9940. SPECIAL_FULLCOLS:   Set this flag if you want the width of the printed picture 
  9941. to be as wide as possible.
  9942.  
  9943.     
  9944. SPECIAL_FULLROWS:   Set this flag if you want the height of the printed picture 
  9945. to be as tall as possible.
  9946.  
  9947.     
  9948. SPECIAL_FRACCOLS:   If this flag is set the "io_DestCols" field specifies the 
  9949. width as a fraction of the maximum width.
  9950.    
  9951.     
  9952. SPECIAL_FRACROWS:   If this flag is set the "io_DestRows" field specifies the 
  9953. height as a fraction of the maximum height.
  9954.  
  9955.     
  9956. SPECIAL_CENTER:     Set this flag if you want the picture to be centered on the 
  9957. paper.
  9958.  
  9959.     
  9960. SPECIAL_ASPECT:     Set this flag if you want to use the correct aspect ratio 
  9961. of the picture. If you set the flag "SPECIAL_FULLCOLS" and this 
  9962. "SPECIAL_ASPECT" flag the printed picture will be as large as possible, but 
  9963. still with the right aspects. 
  9964.  
  9965.     
  9966. SPECIAL_DENSITY1:   Set this flag if you want the picture to be printed with 
  9967. the printer's lowest resolution.
  9968.  
  9969.     
  9970. SPECIAL_DENSITY2:   Next resolution.
  9971.  
  9972.     
  9973. SPECIAL_DENSITY3:   Next resolution.
  9974.  
  9975.     
  9976. SPECIAL_DENSITY4:   Next resolution.
  9977.  
  9978.     
  9979. SPECIAL_DENSITY5:   Next resolution.
  9980.  
  9981.     
  9982. SPECIAL_DENSITY6:   Next resolution.
  9983.  
  9984.     
  9985. SPECIAL_DENSITY7:   Use the printer's highest (finest) resolution.
  9986.    
  9987.     
  9988. SPECIAL_NOFORMFEED: Set this flag if you do not want that the paper is ejected 
  9989. after each time you have printed graphics.
  9990.  
  9991.     
  9992. SPECIAL_TRUSTME:    Set this flag if you do not want the printer to reset any 
  9993. parameters while printing.
  9994.  
  9995. 9.2.1.4  HOW TO CREATE THE PRINTER REQUEST BLOCK
  9996.  
  9997. To create a printer request block, use the CreateExtIO() function. Since there 
  9998. exist three different types of structures that are needed, the printer device 
  9999. assumes that you are using a union of these three structures. The union is 
  10000. sadly not declared in any header file, so you have to declare it yourself. It 
  10001. should look like this:
  10002.  
  10003. union printerIO
  10004. {
  10005.   struct IOStdReq ios;
  10006.   struct IODRPReq iodrp;
  10007.   struct IOPrtCmdReq iopc;
  10008. };
  10009.  
  10010. The CreateExtIO function should always be used when you want to create a 
  10011. request block which is not of the normal size (struct IOStdReq).
  10012.  
  10013. Synopsis
  10014. :
  10015.     
  10016. prt_req = CreateExtIO( msg_port, size );
  10017.  
  10018. prt_req:
  10019.     
  10020. (struct IORequest *) If CreateExtIO() managed to allocate and initialize the 
  10021. request block it returns a pointer to it, else the function will return NULL. 
  10022. (Note that you have to do some "casting" here, since the compiler expects that 
  10023. the function returns a pointer to a normal IORequest structure, but this 
  10024. function is used when you create other (larger) types of request block. Se 
  10025. example for more information.)
  10026.  
  10027. msg_port:
  10028.     
  10029. (struct MsgPort *) Pointer to a message port to which the device will send a 
  10030. message to each time it has completed your request. 
  10031.  
  10032. size:
  10033.     
  10034. The size (in bytes) of the request block. Use the function sizeof() to find out 
  10035. how big the special  request block is. Example: sizeof( union printerIO );
  10036.  
  10037. 9.2.2  OPEN THE PRINTER DEVICE
  10038.  
  10039. As with all devices you have to open a message port through which the printer 
  10040. device can communicate with you, and allocate a request block (a printerIO 
  10041. union), before you may open the device itself.
  10042.  
  10043. 1. Open a message port: (Since it is only our task and the device that will use 
  10044. the message port, we do not need to make it "public", hence no name. Priority 
  10045. should as usual be set to 0, normal priority.)
  10046.  
  10047.      struct MsgPort *replymp;
  10048.  
  10049.      replymp = (struct MsgPort *)
  10050.        CreatePort( NULL, 0 );
  10051.  
  10052.      if( !replymp )
  10053.        clean_up( "Could not create the reply port!" );
  10054.  
  10055. 2. Allocate a request block of type printerIO union. Since the printer request 
  10056. block is larger than normal request blocks (IOStdReq structure) it must be 
  10057. created with the CreateExtIO() function.
  10058.  
  10059.      union printerIO printer_req;
  10060.  
  10061.      printer_req = (union printerIO *)
  10062.        CreateExtIO( replymp, sizeof( union printerIO ) );
  10063.  
  10064.      if( !printer_req )
  10065.        clean_up( "Not enough memory!" );
  10066.  
  10067.  
  10068. 3. Once the message port and the request block have successfully been created 
  10069. you may open the printer device.
  10070.  
  10071.      UBYTE error;
  10072.  
  10073.      /* Open the printer device: */
  10074.      error = OpenDevice( "printer.device", 0, printer_req, 0 );
  10075.  
  10076.      if( error )
  10077.        clean_up( "Could not open the Printer Device!" );
  10078.  
  10079. 9.2.3  PRINT TEXT
  10080.  
  10081. When the printer device has been opened you may start to send text to the 
  10082. printer. When printing normal text you should use the "IOStdReq" structure of 
  10083. the printerIO union. You can either send text which will not be translated (raw 
  10084. text) to the printer, or you can send text that will be translated.
  10085.  
  10086. The advantage of sending text that is translated by Preferences is that the 
  10087. characters you send will also be the same when printed. (Many printers have 
  10088. their own list of foreign characters. And if the characters are not translated, 
  10089. you never know what will be printed.)
  10090.  
  10091. The second reason for translating text is that "escape sequences" (like 
  10092. "underline on", "boldface off", "NLQ" etc...) will be translated into the 
  10093. printer's own printer commands. If you write a program that uses "escape 
  10094. sequences" you know that it will work on all printers (as long as they support 
  10095. your commands). If you on the other hand send special untranslated printer 
  10096. commands that are unlike for some type of printer models, your program will not 
  10097. work with other types of printers. 
  10098.  
  10099. Raw (untranslated) text should only be used when it is really needed (which is 
  10100. not very often). If you want to dump the printers own ASCII codes it can be 
  10101. useful to send untranslated characters, but otherwise I recommend you to only 
  10102. send translated text.
  10103.  
  10104. To print text simply do like this:
  10105.  
  10106. 1. Set the "io_Command" field to CMD_WRITE if you want the text to be 
  10107. translated by Preferences. On the other hand, if you want to send raw 
  10108. (untranslated) text use the "PRD_RAWWRITE" command.
  10109.  
  10110. 2. Set the "io_Length" field to the number of characters (bytes) you want to 
  10111. send.
  10112.  
  10113. 3. Give the "io_Data" pointer the address of your data buffer which contains 
  10114. the text you want to print. 
  10115.  
  10116. 4. Now send the request to the printer device, by either a DoIO() or SendIO() 
  10117. function call.
  10118.      
  10119. If you want to wait for the device to finish your request before your program 
  10120. continues you should use the "synchronous" command DoIO(). If you instead want 
  10121. to continue to do something while the printer is working, you should use the 
  10122. "asynchronous" command SendIO().
  10123.  
  10124. Here is an example on how to send text to the printer. This is a synchronous 
  10125. request, which means the program is halted while the printer is working.
  10126.  
  10127.  
  10128.   /* Pointer to an already initialized printer request block: */
  10129.   union printerIO *printer_req
  10130.  
  10131.   /* The text we want to print: (Oh no, not that text again!) */
  10132.   BYTE data[13] = "Hello world!"
  10133.  
  10134.   /* Store any error values here: */
  10135.   BYTE error;
  10136.  
  10137.  
  10138.   /* We want to print some translated text: (If we want */
  10139.   /* to send raw, untranslated, text we should use the  */
  10140.   /* "PRD_RAWWRITE" command instead of "CMD_WRITE".)    */
  10141.   printer_req->ios.io_Command = CMD_WRITE;
  10142.  
  10143.   /* Give the start address of our data: */
  10144.   printer_req->ios.io_Data = (APTR) data;
  10145.  
  10146.   /* Set number of chracters that should be printed: */
  10147.   /* (12 characters - bytes)                         */
  10148.   printer_req->ios.io_Length = 12;
  10149.  
  10150.   /* Do our request: (This is a task sleep) */
  10151.   error = DoIO( printer_req );
  10152.  
  10153.   /* Check if the request was successfully executed: */
  10154.   if( error )
  10155.     printf( "Problems! Error code: %d\n", error );
  10156.  
  10157. Here is another example, but this time we use an asynchronous request, which 
  10158. means the program continues to run while the printer is working.
  10159.  
  10160.   /* Pointer to an already initialized printer request block: */
  10161.   union printerIO *printer_req
  10162.  
  10163.   /* The text we want to print: */
  10164.   BYTE data[13] = "Hello again!"
  10165.  
  10166.   /* Store any error values here: */
  10167.   BYTE error;
  10168.  
  10169.   /* Temporary pointer: */
  10170.   union printerIO *ptr;
  10171.  
  10172.  
  10173.   /* We want to print some translated text: (If we want */
  10174.   /* to send raw, untranslated, text we should use the  */
  10175.   /* "PRD_RAWWRITE" command instead of "CMD_WRITE".)    */
  10176.   printer_req->ios.io_Command = CMD_WRITE;
  10177.  
  10178.   /* Give the start address of our data: */
  10179.   printer_req->ios.io_Data = (APTR) data;
  10180.  
  10181.   /* Set number of chracters that should be printed: */
  10182.   /* (12 characters - bytes)                         */
  10183.   printer_req->ios.io_Length = 12;
  10184.  
  10185.   /* Do our request and return immediately: */
  10186.   SendIO( printer_req );
  10187.  
  10188.  
  10189.   /* As long as the pointer is not pointing to */
  10190.   /* the request we should stay in the loop:   */
  10191.   ptr = NULL;
  10192.   while( ptr == NULL )
  10193.   {
  10194.     /*   ... do something ...    */
  10195.     /* Well, I do not know what. */
  10196.   
  10197.     /* Check if the request has been completed: (If the  */
  10198.     /* request has been completed CheckIO() will return  */
  10199.     /* a pointer to the request, else NULL is returned.) */
  10200.     ptr = (union printerIO *) CheckIO( printer_req );
  10201.   }
  10202.  
  10203.   /* At last the request was completed! */
  10204.  
  10205.  
  10206.   /* Remove the request block's message. (The ptr and   */
  10207.   /* printer_req are in this example identical, so it   */
  10208.   /* does not matter whichever you use. The parenthesis */
  10209.   /* around the expression is actually unnecessary, but */
  10210.   /* this looks better.)                                */
  10211.   Remove( &(ptr->ios.io_Message.mn_Node) );
  10212.  
  10213.  
  10214.   /* Check if everything is OK? */
  10215.   if( ptr->ios.io_Error )
  10216.     printf( "Problems while printing!\n" );
  10217.  
  10218. 9.2.4  SEND SPECIAL COMMANDS TO THE PRINTER
  10219.  
  10220. Most printers can use different printing styles like underlined, italics and 
  10221. boldface just to mention a few. Usually you can use several different fonts and 
  10222. expand and compress the characters, as well as setting margins, use 
  10223. proportional styles, handle foreign character sets etc...
  10224.  
  10225. All these features are controlled by special printer commands. Most printers 
  10226. have their own set of unique commands. Luckily we do not need to know about 
  10227. them. We simply use a set of universal printer commands listed below, and 
  10228. Preferences will automatically translate these into the specified printer's own 
  10229. commands.
  10230.  
  10231. There exist two ways to send the printer commands to the printer. Either you 
  10232. send the "escape sequences" together with the rest of the text, or you can send 
  10233. the commands with a separate request block (struct IOPrtCmdReq). Since escape 
  10234. sequences are rather difficult to remember it usually looks better if you use 
  10235. the separate request block technique.
  10236.  
  10237. When you send a printer command with help of the request block you should use 
  10238. the IOPrtCmdReq structure. You set the io_Command field to "PRD_PRTCOMMAND", 
  10239. the "io_PrtCommand" field to the command you want to send, and up to four 
  10240. parameters may also be sent if needed by initializing the "io_Parm0" to 
  10241. "io_Parm3" fields. Here is an example:
  10242.  
  10243.   /* We want to send a printer command to the printer: */
  10244.   printer_req->iopc.io_Command = PRD_PRTCOMMAND;
  10245.  
  10246.   /* Set the printer command: [Underline On] */
  10247.   printer_req->iopc.io_PrtCommand = aSGR4;
  10248.  
  10249.   /* Set the parameters: */
  10250.   printer_req->iopc.io_Parm0 = 0; /* Nothing */
  10251.   printer_req->iopc.io_Parm1 = 0; /* Nothing */
  10252.   printer_req->iopc.io_Parm2 = 0; /* Nothing */
  10253.   printer_req->iopc.io_Parm3 = 0; /* Nothing */
  10254.  
  10255.   /* Do the request: */
  10256.   DoIO( printer_req );
  10257.  
  10258. Here is the complete list of printer commands and escape sequences. The names 
  10259. are defined in header file "devices/printer.h".
  10260.  
  10261. If you use the escape sequences you should send the commands exactly as written 
  10262. below with one difference - you should replace the character "n" with a (ASCII) 
  10263. value. To set the top and bottom margins to 2 and 4, send the escape sequence: 
  10264. "ESC[2;4r" (without quotations).
  10265.  
  10266.   Name   Command Esc Seq   Function                Defined by
  10267.   --------------------------------------------------------------
  10268.   aRIS       0   ESCc      reset                   ISO 
  10269.   aRIN       1   ESC#1     initialize              Amiga
  10270.   aIND       2   ESCD      lf                      ISO
  10271.   aNEL       3   ESCE      return,lf               ISO
  10272.   aRI        4   ESCM      reverse lf              ISO
  10273.  
  10274.   aSGR0      5   ESC[0m    normal char set         ISO
  10275.   aSGR3      6   ESC[3m    italics on              ISO
  10276.   aSGR23     7   ESC[23m   italics off             ISO 
  10277.   aSGR4      8   ESC[4m    underline on            ISO 
  10278.   aSGR24     9   ESC[24m   underline off           ISO 
  10279.   aSGR1     10   ESC[1m    boldface on             ISO 
  10280.   aSGR22    11   ESC[22m   boldface off            ISO 
  10281.   aSFC      12   ESC[3nm   set foreground color    ISO 
  10282.   aSBC      13   ESC[4nm   set background color    ISO 
  10283.  
  10284.   aSHORP0   14   ESC[0w    normal pitch            DEC 
  10285.   aSHORP2   15   ESC[2w    elite on                DEC 
  10286.   aSHORP1   16   ESC[1w    elite off               DEC 
  10287.   aSHORP4   17   ESC[4w    condensed fine on       DEC 
  10288.   aSHORP3   18   ESC[3w    condensed off           DEC 
  10289.   aSHORP6   19   ESC[6w    enlarged on             DEC 
  10290.   aSHORP5   20   ESC[5w    enlarged off            DEC 
  10291.  
  10292.   aDEN6     21   ESC[6"z   shadow print on         DEC (sort of) 
  10293.   aDEN5     22   ESC[5"z   shadow print off        DEC 
  10294.   Name   Command Esc Seq   Function                Defined by
  10295.   --------------------------------------------------------------
  10296.   aDEN4     23   ESC[4"z   doublestrike on         DEC 
  10297.   aDEN3     24   ESC[3"z   doublestrike off        DEC 
  10298.   aDEN2     25   ESC[2"z   NLQ on                  DEC 
  10299.   aDEN1     26   ESC[1"z   NLQ off                 DEC 
  10300.  
  10301.   aSUS2     27   ESC[2v    superscript on          Amiga 
  10302.   aSUS1     28   ESC[1v    superscript off         Amiga 
  10303.   aSUS4     29   ESC[4v    subscript on            Amiga 
  10304.   aSUS3     30   ESC[3v    subscript off           Amiga 
  10305.   aSUS0     31   ESC[0v    normalize the line      Amiga 
  10306.   aPLU      32   ESCL      partial line up         ISO 
  10307.   aPLD      33   ESCK      partial line down       ISO 
  10308.   
  10309.                            Char set: or Typface:
  10310.   aFNT0     34   ESC(B       US            0       DEC
  10311.   aFNT1     35   ESC(R       French        1       DEC
  10312.   aFNT2     36   ESC(K       German        2       DEC
  10313.   aFNT3     37   ESC(A       UK            3       DEC
  10314.   aFNT4     38   ESC(E       Danish        4       DEC
  10315.   aFNT5     39   ESC(H       Sweden        5       DEC
  10316.   aFNT6     40   ESC(Y       Italian       6       DEC
  10317.   aFNT7     41   ESC(Z       Spanish       7       DEC
  10318.   aFNT8     42   ESC(J       Japanese      8       Amiga
  10319.   aFNT9     43   ESC(6       Norweign      9       DEC
  10320.   aFNT10    44   ESC(C       Danish II    10       Amiga
  10321.  
  10322.   aPROP2    45   ESC[2p    proportional on         Amiga 
  10323.   aPROP1    46   ESC[1p    proportional off        Amiga 
  10324.   aPROP0    47   ESC[0p    proportional clear      Amiga 
  10325.  
  10326.   aTSS      48   ESC[n E   set proportional offset ISO 
  10327.   aJFY5     49   ESC[5 F   auto left justify       ISO 
  10328.   aJFY7     50   ESC[7 F   auto right justify      ISO 
  10329.   aJFY6     51   ESC[6 F   auto full justify       ISO 
  10330.   aJFY0     52   ESC[0 F   auto justify off        ISO 
  10331.   aJFY3     53   ESC[3 F   letter space (justify)  ISO (special) 
  10332.   aJFY1     54   ESC[1 F   word fill(auto center)  ISO (special) 
  10333.  
  10334.   aVERP0    55   ESC[0z    1/8" line spacing       Amiga 
  10335.   aVERP1    56   ESC[1z    1/6" line spacing       Amiga 
  10336.   aSLPP     57   ESC[nt    set form length n       DEC 
  10337.   aPERF     58   ESC[nq    perf skip n (n>0)       Amiga 
  10338.   aPERF0    59   ESC[0q    perf skip off           Amiga 
  10339.  
  10340.   aLMS      60   ESC#9     Left margin set         Amiga 
  10341.   aRMS      61   ESC#0     Right margin set        Amiga 
  10342.   aTMS      62   ESC#8     Top margin set          Amiga 
  10343.   aBMS      63   ESC#2     Bottom marg set         Amiga 
  10344.  
  10345.   aSTBM     64   ESC[n;nr  T&B margins             DEC 
  10346.   aSLRM     65   ESC[n;ns  L&R margin              DEC 
  10347.   aCAM      66   ESC#3     Clear margins           Amiga 
  10348.   Name   Command Esc Seq   Function                Defined by
  10349.   --------------------------------------------------------------
  10350.   aHTS      67   ESCH      Set horiz tab           ISO 
  10351.   aVTS      68   ESCJ      Set vertical tabs       ISO 
  10352.  
  10353.   aTBC0     69   ESC[0g    Clr horiz tab           ISO 
  10354.   aTBC3     70   ESC[3g    Clear all h tab         ISO 
  10355.   aTBC1     71   ESC[1g    Clr vertical tabs       ISO 
  10356.   aTBC4     72   ESC[4g    Clr all v tabs          ISO 
  10357.   aTBCAL    L73  ESC#4     Clr all h & v tabs      Amiga 
  10358.   aTBSAL    L74  ESC#5     Set default tabs        Amiga 
  10359.   aEXTEN    D75  ESC[n"x   extended commands       Amiga 
  10360.   aRAW      76   ESC[n"r   Next 'Pn' chars are raw Amiga 
  10361.   
  10362. Suggested typefaces are:
  10363.  
  10364. 0 - default typeface.
  10365. 1 - Line Printer or equiv.
  10366. 2 - Pica or equiv.
  10367. 3 - Elite or equiv.
  10368. 4 - Helvetica or equiv.
  10369. 5 - Times Roman or equiv.
  10370. 6 - Gothic or equiv.
  10371. 7 - Script or equiv.
  10372. 8 - Prestige or equiv.
  10373. 9 - Caslon or equiv.
  10374. 10 - Orator or equiv.
  10375.  
  10376. Amiga: Unique command for the Amiga.
  10377. ISO:   Defined by the International Standards Organization.
  10378. DEC:   Defined by Digital Equipment Corporation.
  10379.   
  10380. 9.2.5  PRINT GRAPHICS
  10381.  
  10382. The printer device does not only handle text, it can also be used to print 
  10383. graphics. To do this we have to use the IODRPReq structure of the request 
  10384. block. We give it a pointer to a RastPort which should be printed and sets some 
  10385. special values like size, resolution etc. The rest is then automatically done.
  10386.  
  10387. Note that some printers does not support graphics, and thus your request will 
  10388. be returned immediately with an error value "PDERR_NOTGRAPHICS".
  10389.  
  10390. Here is what we have to do:
  10391.  
  10392. 1. Set the command "PRD_DUMPRPORT".
  10393.  
  10394. 2. Give the request block a pointer to the RastPort we want to print.
  10395.  
  10396. 3. We must also give the request block a pointer to a ColorMap structure which 
  10397. contains the picture's colour values.
  10398.  
  10399. 4. Tell the request block what screen "mode" the RastPort is made for. The 
  10400. information is used to convert the picture which will be printed to the correct 
  10401. aspects.
  10402.      
  10403. On a low resolution screen each pixels is equally wide as tall. However, on a 
  10404. high resolution screen, each pixel is only half as wide as it is tall. The same 
  10405. applies for interlaced and non interlaced screens.
  10406.      
  10407. The printer device must also know if you want to print a "normal" picture, or a 
  10408. picture with one of the Amiga's special display modes like "HAM" or "Extrahalf 
  10409. Brite".
  10410.      
  10411. If you print a RastPort that is connected to a ViewPort, you can simply copy 
  10412. the Viewport structure's "Modes" field. If you have defined a RastPort that is 
  10413. not used to be displayed just to be printed, set the field to 0.
  10414.  
  10415. 5. Tell the device if the whole RastPort should be printed, or just a part of 
  10416. it. 
  10417.  
  10418. 6. You must also specify how large the printed picture should be. Note that 
  10419. most printers have a much higher resolution than normal screens. You can 
  10420. therefore usually print a picture that is much smaller than the screen, and 
  10421. still have all details as the original.
  10422.  
  10423. 7. Finally you can set some special graphical flags as described above.
  10424.  
  10425. Here is an example:
  10426.  
  10427.   /* Store possible error numbers here: */
  10428.   BYTE error;
  10429.  
  10430.   /* An already initialized printer request block: */
  10431.   union printerIO *printer_req,
  10432.  
  10433.   /* Pointer to a RastPort: */
  10434.   struct RastPort *rp,
  10435.  
  10436.   /* Pointer to a ColourMap: */
  10437.   struct ColorMap *cm,
  10438.  
  10439.  
  10440.   /* We want to dump a RastPort to the printer: */
  10441.   printer_req->iodrp.io_Command = PRD_DUMPRPORT;
  10442.  
  10443.   /* Set a pointer to the RastPort structure: */
  10444.   printer_req->iodrp.io_RastPort = rp;
  10445.  
  10446.   /* Set a pointer to the ColorMap structure: */
  10447.   printer_req->iodrp.io_ColorMap = cm;
  10448.  
  10449.   /* Set the "display" modes:         */
  10450.   /* (0 = aspect 1:1, normal screen.) */
  10451.   printer_req->iodrp.io_Modes = 0;
  10452.  
  10453.   /* X position of the source: (10 pixels out) */
  10454.   printer_req->iodrp.io_SrcX = 10;
  10455.  
  10456.   /* Y position of the source: (10 pixels down) */
  10457.   printer_req->iodrp.io_SrcY = 10;
  10458.  
  10459.   /* Width of the source: (100 pixels wide) */
  10460.   printer_req->iodrp.io_SrcWidth = 100;
  10461.  
  10462.   /* Height of the source: (50 pixels high) */
  10463.   printer_req->iodrp.io_SrcHeight = 50;
  10464.  
  10465.   /* The width of the printed picture: (This field is in     */
  10466.   /* this example ignored by the printer device since the    */
  10467.   /* special flag "SPECIAL_FULLCOLS" is set. Maximum width.) */ 
  10468.   printer_req->iodrp.io_DestCols = 0;
  10469.  
  10470.   /* The height of the printed picture: (This field is in  */
  10471.   /* this example also ignored by the printer device since */
  10472.   /* the special flag "SPECIAL_ASPECT" is set. The height  */
  10473.   /* will therefore be automatically calculated.)          */ 
  10474.   printer_req->iodrp.io_DestRows = 0;
  10475.  
  10476.   /* Set the special printing commands: (Full width and     */
  10477.   /* the height is automatically set so the printed picture */
  10478.   /* will have the right aspects.)                          */
  10479.   printer_req->iodrp.io_Special =
  10480.     SPECIAL_FULLCOLS | SPECIAL_ASPECT;
  10481.  
  10482.  
  10483.   /* Do our request: */
  10484.   error = DoIO( printer_req );
  10485.  
  10486.   /* Check if the request was successfully executed: */
  10487.   if( error )
  10488.     printf( "Problems while printing!\n" );
  10489.  
  10490. 9.2.6  ERRORS
  10491.  
  10492. When you are using the printer device it sometimes happens that your request 
  10493. was not successfully executed, and you have encounter an error message. Usually 
  10494. it is easy to guess what went wrong, but it is always good to check what really 
  10495. happened.
  10496.  
  10497. You will either receive the error message from the function you just called 
  10498. (for example, DoIO() returns 0 or an error number), or you can check the 
  10499. request block to see if there were any problems. (The io_Error filed of the 
  10500. request block either contains 0, which means everything is OK, or an error 
  10501. number.)
  10502.  
  10503. Here is a complete list of the printer device error messages:
  10504.  
  10505. PDERR_CANCEL:
  10506.     
  10507. The user cancelled the request. If you receive this error you should not start 
  10508. new printer requests until the user says he/she wants to print again. It is 
  10509. very annoying if you have cancelled a printout but the program continues to 
  10510. write new data to the printer. If you have issued several asynchronous requests 
  10511. after each other and you receive this message, it is usually best to try to 
  10512. remove the queued requests.
  10513.  
  10514. PDERR_NOTGRAPHICS:
  10515.     
  10516. The printer which the user has can not handle graphics.    
  10517.  
  10518. PDERR_BADDIMENSION:
  10519.     
  10520. The dimension of the printout is not valid.
  10521. PDERR_INTERNALMEMORY:
  10522.     
  10523. Not enough memory for the printer device's internal variables.
  10524.  
  10525. PDERR_BUFFERMEMORY:
  10526.     
  10527. Not enough memory for the print buffer.
  10528.  
  10529. While you are using the printer device it may happen that you also receive 
  10530. error messages from Exec. (Exec is handling all stuff like messages, requests, 
  10531. tasks and so on.) Here is a complete list of exec error messages: (defined in 
  10532. the header file "exec/errors.h")
  10533.  
  10534. IOERR_OPENFAIL:
  10535.     
  10536. The device (unit) could not be opened.
  10537.   
  10538. IOERR_ABORTED:
  10539.     
  10540. When you abort a previously started request by calling the AbortIO() function, 
  10541. the "io_Error" filed of that request is set to "IOERR_ABORTED". If you find a 
  10542. request block with this flag set, you know that it has been aborted.
  10543.   
  10544. IOERR_NOCMD:
  10545.     
  10546. You tried to use a command that is not supported by the printer device.
  10547.   
  10548. IOERR_BADLENGTH:
  10549.     
  10550. The length of the request was not valid.
  10551.  
  10552. 9.2.7  CLEAN UP
  10553.  
  10554. As usual on the Amiga you must remember to close and return everything you have 
  10555. opened or allocated. If you do not close the printer device after you a lot of 
  10556. memory is wasted. The routine is very similar to how you close the parallel 
  10557. device which was described in the previous chapter.
  10558.  
  10559. Here is a list of what you have to do:
  10560.  
  10561. 1. All requests you have started with SendIO() or BeginIO() (asynchronous 
  10562. commands) must either have been completed or aborted before you may close the 
  10563. device. It is a very common error to forget this, and it can be hard to find 
  10564. this bug. Usually the program will work fine (the command was completed in 
  10565. time), but now and then your program will crash (the command was completed 
  10566. after the device have been closed).
  10567.  
  10568. A simple way is to abort all commands that have not reported that they have 
  10569. been completed, but this is not always good way to do it. (The last commands 
  10570. may be important and should therefore not be aborted.)
  10571.      
  10572. If you do not want to abort the command, you should instead wait for it to be 
  10573. completed. The WaitIO() function is simple to use, and will put your program to 
  10574. sleep while waiting, so no computer time is wasted. If the request has already 
  10575. been completed, the function will return immediately. WaitIO() will also remove 
  10576. the message from the reply port. It is a very useful and simple function to 
  10577. use, but do NOT try to wait for a request that has not been started!
  10578.  
  10579. Here is an example on how to wait for a request to be completed: (If the 
  10580. request already has been completed it does not matter, WaitIO() will then 
  10581. simply return immediately. Note that we do not have to remove any messages from 
  10582. the reply port if we use WaitIO().)
  10583.  
  10584.        /* Store possible error numbers here: */
  10585.        UBYTE error;
  10586.      
  10587.        /* ... */
  10588.      
  10589.        /* Wait for the request to be completed: */
  10590.        error = WaitIO( printer_req );
  10591.      
  10592.        /* Everything OK? */
  10593.        if( error )
  10594.          printf( "Something went wrong!" );
  10595.  
  10596.        /* Well, successful or not, we may now */
  10597.        /* close the device!                   */
  10598.  
  10599. To abort a request, simply use the AbortIO() function:
  10600.  
  10601.        /* Try to abort a previously started request:  */
  10602.        /* (Do not try to abort a request that has not */
  10603.        /* been started!)                              */
  10604.        AbortIO( printer_req );
  10605.  
  10606. 2. When all requests have been completed or aborted you may close the printer 
  10607. device. (Requests that have been started by calling the DoIO() function have 
  10608. already been completed before your program wakes up, and thus you do not need 
  10609. to wait for these.)
  10610.      
  10611. The printer device is closed as all other devices, by calling the CloseDevice() 
  10612. function. Here is an example:
  10613.  
  10614.        /* Close the Printer Device: */ 
  10615.        CloseDevice( printer_req );
  10616.  
  10617. 3. You should now return all request blocks you have allocated. The printer 
  10618. request block (union printerIO) is an extended request block, and should be 
  10619. deleted by the DeleteExtIO() function.
  10620.  
  10621.      /* Deallocate the printer request block: */
  10622.      DeleteExtIO( printer_req, sizeof(union printerIO) );
  10623.      
  10624. Note that ALL request blocks that have been allocated, must be removed!
  10625.  
  10626. 4. Finally you should close all message ports you have previously opened. 
  10627. Simply use the DeletePort() function as this example demonstrates:
  10628.  
  10629.        /* Remove the replyport: */
  10630.        DeletePort( replymp);
  10631.  
  10632. Please be careful with how your program terminates! Your program should not 
  10633. only run fine, but it should also allow other programs to run after and 
  10634. simultaneously. Remember that your program must also be able to quit nice and 
  10635. neatly even if it had to terminate too early because of some fatal error. The 
  10636. cleaning up should only be done where it is needed, and if you have not 
  10637. allocated the memory or opened the device before your program quits, you should 
  10638. of course NOT try to free these resources! If you do the Amiga will most 
  10639. certainly crash! Too many programs contain this very annoying error. Make sure 
  10640. yours will not be one of those.
  10641.  
  10642. 9.3  A COMPLETE EXAMPLE
  10643.  
  10644. Here is a simple but complete example on how to print some text. For more 
  10645. information see the Examples which are stored together with this document.
  10646.  
  10647.  
  10648. #include <exec/types.h>       /* Data types.             */
  10649. #include <exec/errors.h>      /* Exec error messages.    */
  10650. #include <devices/printer.h>  /* Printer Device.         */
  10651. #include <exec/io.h>          /* Standard request block. */
  10652.  
  10653.  
  10654. /* Declare how the printer request block look like: */
  10655. union printerIO
  10656. {
  10657.   struct IOStdReq ios;
  10658.   struct IODRPReq iodrp;
  10659.   struct IOPrtCmdReq iopc;
  10660. };
  10661.  
  10662.  
  10663. /* Declare a pointer to our reply port: */
  10664. struct MsgPort *replymp = NULL;
  10665.  
  10666. /* Declare a pointer our printer request block: */
  10667. union printerIO *printer_req = NULL;
  10668.  
  10669. /* Store the printer device error here: */
  10670. UWORD printer_dever = TRUE;
  10671.  
  10672. /* Declare our data buffer: (25 characters) */
  10673. BYTE buffer[] = "Anders Bjerin was here...";
  10674.  
  10675.  
  10676. /* Declare our functions: */
  10677. void main();
  10678. void clean_up( STRPTR text );
  10679.  
  10680.  
  10681. void main()
  10682. {
  10683.   /* Error number: */
  10684.   BYTE error;
  10685.   
  10686.   
  10687.   /* Get a reply port: (No name, priority 0) */
  10688.   replymp = (struct MsgPort *)
  10689.     CreatePort( NULL, 0 );
  10690.   if( !replymp )
  10691.     clean_up( "Could not create the reply port!" );
  10692.  
  10693.  
  10694.   /* Create the printer request block: */
  10695.   printer_req = (union printerIO *)
  10696.     CreateExtIO( replymp, sizeof(union printerIO) );
  10697.   if( !printer_req )
  10698.     clean_up( "Not enough memory for the printer request block!" );
  10699.  
  10700.  
  10701.   /* Open the Printer Device: */
  10702.   printer_dever = OpenDevice( "printer.device", 0, printer_req, 0 );
  10703.   if( printer_dever )
  10704.     clean_up( "Could not open the Printer Device!" );
  10705.  
  10706.  
  10707.  
  10708.   /* We want to print some text: (translated) */
  10709.   printer_req->ios.io_Command = CMD_WRITE;
  10710.  
  10711.   /* Give the start address of our data: */
  10712.   printer_req->ios.io_Data = (APTR) buffer;
  10713.  
  10714.   /* Set number of chracters that should be printed: */
  10715.   printer_req->ios.io_Length = 25;
  10716.  
  10717.   /* Do our request: */
  10718.   error = DoIO( printer_req );
  10719.  
  10720.   /* Check if the request was successfully executed: */
  10721.   if( error )
  10722.     printf( "Problems while printing!" );
  10723.  
  10724.  
  10725.   /* Clean up and quit: */
  10726.   clean_up( "The End!" );
  10727. }
  10728.  
  10729.  
  10730. /* Close and return everything that has been */
  10731. /* opened and allocated before we quit:      */
  10732.  
  10733. void clean_up( STRPTR text )
  10734. {
  10735.   /* Close the Printer Device: */ 
  10736.   if( !printer_dever )
  10737.     CloseDevice( printer_req );
  10738.  
  10739.   /* Deallocate the printer request block: */
  10740.   if( printer_req )
  10741.     DeleteExtIO( printer_req, sizeof(union printerIO) );
  10742.  
  10743.   /* Remove the replyport: */
  10744.   if( replymp )
  10745.     DeletePort( replymp);
  10746.  
  10747.   /* Print the message: */
  10748.   printf( "\n%s\n", text );
  10749.  
  10750.   /* Quit: */
  10751.   exit( 0 );
  10752. }
  10753.  
  10754.  
  10755. 9.4  OTHER USEFUL COMMANDS
  10756.  
  10757. There exist four more printer request commands that may sometimes be useful:
  10758.  
  10759. 1. Flush, removes all queued requests.
  10760. 2. Reset, reinitializes the printer device.
  10761. 3. Start, restarts the printer communication.
  10762. 4. Stop, temporary stops the printer communication.
  10763.  
  10764. 9.4.1  FLUSH
  10765.  
  10766. If several requests are sent to the printer device they are all queued on a 
  10767. FIFO (First In First Out) basis. The command "CMD_FLUSH" can then be used to 
  10768. remove all these queued commands. Here is an example:
  10769.  
  10770.   /* We want to remove all queued requests: */
  10771.   printer_req->ios.io_Command = CMD_FLUSH;
  10772.  
  10773.   /* Do our request: */
  10774.   error = DoIO( printer_req );
  10775.  
  10776.   /* OK? */
  10777.   if( error )
  10778.     printf( "Could not remove the queued requests!\n" );
  10779.  
  10780. 9.4.2  RESET
  10781.  
  10782. Send the command "CMD_RESET" to reset the printer device. All commands that are 
  10783. queued to the device will be removed, the command that is currently executed 
  10784. will be aborted, and all printer flags are resetted. Here is an example:
  10785.  
  10786.   /* We want to reset the printer device: */
  10787.   printer_req->ios.io_Command = CMD_RESET;
  10788.  
  10789.   /* Do our request: */
  10790.   error = DoIO( printer_req );
  10791.  
  10792.   /* OK? */
  10793.   if( error )
  10794.     printf( "Could not reset the printer device!\n" );
  10795.  
  10796. 9.4.3  START
  10797.  
  10798. After you have stopped the printer communication by sending an CMD_STOP 
  10799. command, you may want to start the communication again. It is done by sending a 
  10800. CMD_START command. Here is an example:
  10801.  
  10802.   /* We want to start printer communication again: */
  10803.   printer_req->ios.io_Command = CMD_START;
  10804.  
  10805.   /* Do our request: */
  10806.   error = DoIO( printer_req );
  10807.  
  10808.   /* OK? */
  10809.   if( error )
  10810.     printf( "Could not restart the printer communication!\n" );
  10811.  
  10812. 9.4.4  STOP
  10813.  
  10814. To temporary stop all printer communication you send a CMD_STOP command. The 
  10815. communication will then first start again when a CMD_START command is 
  10816. broadcasted. Here is an example:
  10817.  
  10818.   /* We want to temporary stop all printer communication: */
  10819.   printer_req->ios.io_Command = CMD_STOP;
  10820.  
  10821.   /* Do our request: */
  10822.   error = DoIO( printer_req );
  10823.  
  10824.   /* OK? */
  10825.   if( error )
  10826.     printf( "Could not stop the printer communication!\n" );
  10827.  
  10828. 9.5  FUNCTIONS
  10829.  
  10830. DoIO()
  10831.  
  10832. DoIO() is used to send requests to a device, and waits for it to be completed. 
  10833. While the program is waiting it is put to sleep so it will not waste any 
  10834. computer time. DoIO() will return first when the request have been completed or 
  10835. failed, and no message is therefore sent to the reply port.
  10836.  
  10837. Synopsis:
  10838.     
  10839. error = DoIO( req );
  10840.  
  10841. error:
  10842.     
  10843. (long) DoIO() will return first when the request has been completed or 
  10844. something has failed. If the request was successfully completed zero is 
  10845. returned, else an error number is returned. What error number depends on which 
  10846. device was used.
  10847.  
  10848. req:
  10849.     
  10850. (struct IORequest *) Pointer to the request you want to have executed.
  10851.  
  10852. SendIO()
  10853.  
  10854. SendIO() is used to send requests to a device, but will return immediately 
  10855. without any delay. To check if the request have been completed use the 
  10856. CheckIO() function, or look at the request's reply port for any messages. Once 
  10857. the request has been completed you must remove the message at the reply port. 
  10858. (CheckIO() will not do it.) To remove a message use the function Remove(). Note 
  10859. that you may NOT close the device before all requests have been completed or 
  10860. aborted!
  10861.  
  10862. Synopsis:
  10863.     
  10864. SendIO( req )
  10865.  
  10866. req:
  10867.     
  10868. (struct IORequest *) Pointer to the request you want to have executed.
  10869.  
  10870. CheckIO()
  10871.  
  10872. CheckIO() is used to check if a previously started request has been completed. 
  10873. Note that this function will not remove the message at the reply port. This 
  10874. must be done with the Remove() function.
  10875.  
  10876.  
  10877. Synopsis:
  10878.     
  10879. ptr = CheckIO( req );
  10880.  
  10881. ptr:
  10882.     
  10883. (long) CheckIO() will either return NULL if the request have not been completed 
  10884. or it will return a pointer to the request block.
  10885.  
  10886. req:
  10887.     
  10888. (struct IORequest *) Pointer to the request you want to check.
  10889.  
  10890. WaitIO()
  10891.  
  10892. WaitIO() will wait for the request to be completed, and while the program is 
  10893. waiting it is put to sleep so no computer time is wasted.
  10894.  
  10895. Synopsis:
  10896.     
  10897. error = WaitIO( req );
  10898.  
  10899. error:
  10900.     
  10901. (long) WaitIO() will return first when the request, that has previously been 
  10902. sent, has been completed or something has failed. If the request was 
  10903. successfully completed zero is returned, else an error number is returned. What 
  10904. error number depends on which device was used.
  10905.  
  10906. req:
  10907.     
  10908. (struct IORequest *) Pointer to the request you want to wait for to be 
  10909. completed. Note that the request must have already been sent to the device by 
  10910. either a SendIO() or BeginIO() function call.
  10911.  
  10912. AbortIO()
  10913.  
  10914. AbortIO() will try to abort a previously started request. This function should 
  10915. be used sparsely since it does not look so good if you start a request and the 
  10916. try to stop it. (Better not start it at all.) However, it is easy, and can 
  10917. sometimes be very useful.
  10918.  
  10919. A request that is aborted will have its io_Error field set to IOERR_ABORTED 
  10920. (defined in header file "exec/errors.h").
  10921.  
  10922. Synopsis:
  10923.     
  10924. AbortIO( req )
  10925.  
  10926. req:
  10927.     
  10928. (struct IORequest *) Pointer to the request you want to abort.
  10929.  
  10930. CloseDevice()
  10931.  
  10932. CloseDevice() will close a device. Note that you should NOT close the device 
  10933. before all started asynchronous requests have either been completed or aborted.
  10934.  
  10935. Synopsis:
  10936.     
  10937. CloseDevice( ioreq );
  10938.  
  10939. ioreg:
  10940.     
  10941. (struct IORequest *) Pointer to the device's request block.
  10942.  
  10943. OpenDevice()
  10944.  
  10945. OpenDevice() will try to open the specified device.
  10946.   
  10947. Synopsis:
  10948.     
  10949. error = OpenDevice( name, unit, req, flags );
  10950.  
  10951. error:
  10952.     
  10953. (long) If OpenDevice() managed to open the device it returns 0, else an error 
  10954. number is returned.
  10955.  
  10956.   name:
  10957.     
  10958. (char *) Name of the device you want to open. The name of the printer device is 
  10959. "printer.device".
  10960.  
  10961.   unit:
  10962.     
  10963. (long) Set this field to 0.
  10964.  
  10965.   req:
  10966.     
  10967. (struct IORequest *) Pointer to an already initialized printerIO union.
  10968.  
  10969.   flags:
  10970.     
  10971. (long) Set this field to 0.
  10972.  
  10973. 9.6  COMMANDS
  10974.  
  10975. Here is a complete list of commands you may send to the printer device. For 
  10976. full documentation se examples above.
  10977.  
  10978. The special printer device commands: (Defined in header file 
  10979. "devices/printer.h")
  10980.  
  10981. PRD_RAWWRITE
  10982.     
  10983. Send untranslated characters to the printer.
  10984. PRD_PRTCOMMAND
  10985.     
  10986. Send a printer command.
  10987. PRD_DUMPRPORT
  10988.     
  10989. Print graphics. (Dump a RastPort to the printer.)
  10990.  
  10991. The rest of the commands you may use are normal exec commands, and are defined 
  10992. in header file "exec/io.h".
  10993.  
  10994. CMD_RESET
  10995.     
  10996. Resets all parameters of the printer device.
  10997. CMD_WRITE
  10998.     
  10999. Send data to the printer (through preferences).
  11000. CMD_STOP
  11001.     
  11002. Temporary stops all printer communication.
  11003. CMD_START
  11004.     
  11005. Restarts printer communication.
  11006. CMD_FLUSH
  11007.     
  11008. Removes all queued requests.
  11009.  
  11010. 9.7  EXAMPLES
  11011.  
  11012. Example 1
  11013. This program demonstrates how you can use the Printer Device to send (raw as 
  11014. well as translated) text to a printer.
  11015.  
  11016. Example 2
  11017. This program demonstrates how you can use the Printer Device to send (raw as 
  11018. well as translated) text to a printer. However, instead of waiting for our 
  11019. request to be completed as in Example 1, we use asynchronous requests.
  11020.  
  11021. Example 3
  11022. This program demonstrates how you can send printer commands to the Printer 
  11023. Device, which will translate these commands with help of Preferences, before 
  11024. they are sent to the printer.
  11025.  
  11026. Example 4
  11027. This example demonstrates how you can print graphics. It will dump the 
  11028. workbench's Rastport to the printer.
  11029.  
  11030.  
  11031.